2016-05-31 20 views
-1

我有一個接受指針的函數,並返回一個enum取決於與該指針一些條件:如何確保函數不會獲取垃圾指針?

my_enum function(char* prt) 
{ 
    /* function body*/ 
    if (condition1) return enum1; 
    if (condition2) return enum2; 
    if (condition3) return enum3; 
    if (condition4) return enum4; 
    else return enum5; 
} 

我有另一個功能,其還接受一個指針,調用my_function和反應所獲得的值:

void another_function(char* ptr) 
{ 
my_enum result = function(ptr); 
if (result == MY_VALUE) std::cout<<"OK"<<endl; 
} 

我正在運行Valgrind檢查內存泄漏。上面的代碼導致以下錯誤:

Conditional jump depends on an uninitialized variable.

事實上,這是可能的未初始化的指針傳遞給函數功能

我的問題是:處理這種情況的最佳方式是什麼(除了使用引用)?我無法確定每個使用該代碼的人都會初始化他將傳遞給該函數的指針。如果指針指向一些垃圾(我正在檢查它是否爲空指針),我無法在內部檢查函數。

我應該忽略這樣的錯誤嗎?如果它們毫無用處,Valgrind爲什麼要麻煩告訴我他們呢?必須有我能做的事情。

+8

你確定它是初始化的指針嗎? valgrind消息的結果就像'result'未初始化一樣,如果'function'退出而沒有返回,就會發生這種情況。 – delnan

+2

您的返回枚舉的函數仍然可以有一個分支,其中枚舉未初始化。我們無法看到導致valgrind錯誤的代碼。 – stefaanv

+0

我相信*函數*不會退出而不返回。我不能發佈完整的代碼,但有一個* if-else *語句。 – user2738748

回答

1

你願意走多遠?如果有人想要破壞你的代碼,他們會,你無法幫助它。

您應用的更有效的保護越難以得到。

一個簡單的是檢查NULL。這並不妨礙愚蠢的指針,但它可以防止有意識地失效。大多數人都對此感到滿意。

然後你可以給這個指針一個包裝類。實例化這個類需要指向一個有效的對象(或者通過一些無望的跳躍來使它成爲一個無效的對象,這等於有意識地拍攝你的腳),所以不會出現未初始化指針的情景 - 但是對象可以在其之前不再存在指針被使用。

然後,您可以維護這些對象及其指針的工廠/管理器類。每當指針目標對象被創建或銷燬時,其指針都會被創建或失效。除非您的代碼是多線程的,否則這將是無法驗證的,並且在您的函數已經通過檢查並且使用驗證值之前可能會發生破壞。

然後,您可以添加線程安全性,將您的函數和管理器包裝在互斥體中。這增加了與死鎖和同步相關的各種頭痛。但用戶必須非常努力地創建一個派生自您的類(可能首先使用#define private public),以覆蓋其安全功能......

隨着每一步,您的開銷攀升到效果真正停止值得努力的水平。所以,只需檢查該指針是否爲NULL,並停止擔心其他人爲您提供幫助。

0

基本上有兩種解決方案。

  1. 期望一個有效的指針和狀態,清楚地在你的API文檔中。那麼任何無效使用都會導致UB,但這不是你的錯。但是,處理原始指針是C風格,並且被C++程序員所詬病。

  2. 採取(參考到)封裝的指針類型,這總是明智地初始化,例如std::string(代替const char*std::unique_ptr,或std::shared_ptr。例如,

    my_enum function(std::string const&str) 
    { 
        /* function body*/ 
        if (str.empty()) // deal with improper input 
        std::cerr<<"warning: empty string in function()"<<std::endl; 
        if (condition1) return enum1; 
        if (condition2) return enum2; 
        if (condition3) return enum3; 
        if (condition4) return enum4; 
        else return enum5; 
    } 
    

    my_enum function(std::unique_ptr<SomeType> const&ptr) 
    { 
        /* function body*/ 
        if (!ptr) { // deal with improper input 
        std::cerr<<"warning: invalid pointer in function()"<<std::endl; 
        return enum_error; 
        } 
        if (condition1) return enum1; 
        if (condition2) return enum2; 
        if (condition3) return enum3; 
        if (condition4) return enum4; 
        else return enum5; 
    } 
    

    這避免了原始指針,是處理這種情況的C++的方式。後者代碼的一個問題是它只適用於unique_ptr參數。人們可以概括這是重載(使用SFINAE或其他方式)以獲取(const引用)像對象的任何自動指針(例如,定義爲對象obj,成員obj::get() const返回const obj::element_type*)。

+0

'std :: unique_ptr'在這裏沒有什麼可做的,因爲參數的生命週期和管理都不是這個函數的業務。 – Quentin

+0

@Quentin對一個'unique_ptr'採取一個const引用不允許任何干擾指向的對象(包括它的生命期和管理),但只允許const訪問。 – Walter

+0

的確,但你迫使調用者無緣無故地使用'std :: unique_ptr'。如果他使用'std :: shared_ptr',一個'boost :: scoped_ptr'或者只是一個具有自動生命期的對象呢? – Quentin

1

對於什麼是「最佳」方法,意見會有所不同,因爲根本不可能阻止某人通過壞的指針(例如未初始化的,懸掛的)指針。

一個常見的解決方案是完全避免原始指針,並以不接受指針的方式編寫該函數。

一種方法是接受參考。編寫代碼以便它根本不使用原始指針,這使得使用錯誤的參數調用函數變得更加困難。限制是調用者仍然可以創建一個錯誤的引用(例如,通過解引用一個錯誤的指針),但是它需要更多的努力(或者如果不知情完成,需要更長的錯誤序列)來傳遞對函數的錯誤引用,而不是通過壞指針。

另一種方法是通過值(或引用,在某些情況下)接受某些類對象來保存指針。然後執行全部這樣的成員函數,以防止持有不良指針的情況。給這個類沒有接受指針的成員函數。確保構造函數和其他成員函數保持一致性(正式地,構造函數建立了一組嚴格的不變量,其他成員函數維護這些不變量)。這包括如果嘗試使用錯誤數據構造對象(如果在構造對象的過程中拋出異常,該對象從不存在,並且無法以任何方式傳遞給您的函數),則拋出異常。因此,您的函數可以假設 - 如果它被成功調用 - 它收到的數據是有效的。

問題是,上述情況會導致無意中將不良數據傳遞給您的函數。沒有什麼技術可以絕對防止一個足夠堅決的人(無論是通過天才還是愚蠢)找到一種方法來繞過所有的安全措施,並將錯誤的數據傳遞給你的功能。