2010-02-23 123 views
9

宣佈我知道這不會起作用,因爲當函數返回時變量x被破壞:返回指針數據功能

int* myFunction() 
{ 
    int x = 4; return &x; 
} 

讓我怎麼正確地返回一個指向我在函數中創造的東西,我需要注意什麼?我如何避免內存泄漏?

我也用的malloc:

int* myFunction2() 
{ 
    int* x = (int*)malloc(sizeof int); *x = 4; return x; 
} 

你如何正確地做到這一點 - 在C和C++?

+3

你說得對,答案是'myFunction2()'你只需要記住稍後釋放你的記憶。這就是沒有垃圾收集器的問題 – tzenes

+4

再次,雖然C和C++共享許多功能,但根據語言的不同,有很多問題的答案完全不同。如何避免內存泄漏是其中之一,因爲它是如何在函數內部創建內容的......您真正感興趣的語言是什麼? –

回答

6

你的第二個方法是正確的。你只需要清楚地記錄調用者「擁有」結果指針,並負責釋放它。

由於這種額外的複雜性,這是難得的「小」類型如int做到這一點,但我假設你只是用一個int這裏的一個例子的緣故。

有些人也會喜歡拿一個指向已分配對象作爲參數,而不是在內部分配的對象。這更清楚地表明調用者負責解除分配對象(因爲他們首先分配它),但是使得調用站點更加冗長,所以這是一種折衷。

5

對於C++,在許多情況下,只是返回值。即使在較大物體的情況下,RVO也會經常避免不必要的複製。

1

在C++中,你應該使用new

int *myFunction() 
{ 
    int blah = 4; 
    return new int(blah); 
}

而要擺脫它,使用delete:

int main(void) 
{ 
    int *myInt = myFunction(); 
    // do stuff 
    delete myInt; 
}

請注意,我調用拷貝構造函數INT同時使用new ,所以值「4」被複制到堆內存中。獲得指向棧上可靠指針的唯一方法是通過正確調用new將其複製到堆上。

編輯:作爲另一種回答指出,還需要記錄指針需要被調用者後來獲釋。否則,你可能會發生內存泄漏。

2

C++方法來避免內存泄漏。 (至少當你忽略功能輸出)

std::auto_ptr<int> myFunction() { 
    std::auto_ptr<int> result(new int(4)); 
    return result; 
} 

然後調用它:

std::auto_ptr<int> myFunctionResult = myFunction(); 

編輯:正如喬爾指出。 std :: auto_ptr有它自己的缺點,通常應該避免。 而不是std :: auto_ptr你可以使用boost :: shared_ptr(std :: tr1 :: shared_ptr)。

boost::shared_ptr<int> myFunction() { 
    boost::shared_ptr<int> result(new int(5)); 
    return result; 
} 

或者當使用C++ 0x符合編譯器時您可以使用std :: unique_ptr。

std::tr1::unique_ptr<int> myFunction() { 
    std::tr1::unique_ptr<int> result(new int(5)); 
    return result; 
} 

的主要區別在於:

  • shared_ptr的允許的shared_ptr指示的多個實例,以相同的RAW指針。它使用引用計數機制來確保只要存在至少一個shared_ptr實例,內存就不會被釋放。

  • unique_ptr只允許其一個實例持有指針,但具有與auto_ptr不同的真正移動語義。

+0

auto_ptr通常是皺眉,因爲它有奇怪的語義。當你將這個結果傳遞給一個函數時,你不再擁有它並且不能再實際訪問它。 – Joel

+0

你是對的,我完全同意。這就是爲什麼我寫了(至少當你忽略函數輸出時)。 更好的解決方案是例如boost :: shared_ptr,但我建議目前標準化的解決方案。 並且我知道std :: tr1 :: shared_ptr – lollinus

+0

@Joel:如果將它傳遞給另一個函數,該函數可能不會採用auto_ptr。所以你會使用'f(myFunctionResult.get())'並且仍然保留所有權。如果該函數確實需要auto_ptr,則顯然擁有權已轉移。 –

7

對於C++,您可以使用智能指針來執行所有權轉移。 auto_ptrboost::shared_ptr是很好的選擇。

3

一種可能性是傳遞函數指針:

void computeFoo(int *dest) { 
    *dest = 4; 
} 

這是很好的,因爲你可以使用這樣的功能有自動變量:

int foo; 
computeFoo(&foo); 

通過這種方法,你還留着記憶管理在代碼的相同部分,即。

// Compare this: 
int *foo = malloc(…); 
computeFoo(foo); 
free(foo); 

// With the following: 
int *foo = computeFoo(); 
free(foo); 

在它更容易自由忘記,你看不到的malloc第二種情況:只是因爲它發生的地方里面的函數,你不能錯過的一個malloc。這通常至少部分地按照慣例解決,例如:「如果函數名稱以XY開頭,則表示您擁有它返回的數據。」

返回指向「函數」變量指針的有趣角落案例是聲明可變靜態:

int* computeFoo() { 
    static int foo = 4; 
    return &foo; 
} 

當然,這對正常編程來說是邪惡的,但它有一天可能會派上用場。

+0

靜態是好的;它的悲傷不是= malloc – xealits

1

還有另一種方法 - 聲明x靜態。在這種情況下,它將位於數據段中,而不是堆棧中,因此它在程序運行時期間是可用的(並且是持久的)。

int *myFunction(void) 
{ 
    static int x = 4; 
    return &x; 
} 

請注意,分配x=4將只對myFunction第一次調用執行:

int *foo = myFunction(); // foo is 4 
*foo = 10;     // foo is 10 
*foo = myFunction();  // foo is 10 

NB!使用函數範圍靜態變量不是行車安全技術。

+0

爲什麼downvote?我的回答完全正確。 – qrdl

0

Boost或TR1共享指針通常是要走的路。它避免了複製開銷,並且給你半自動刪除。所以你的功能應該是這樣的:

boost::shared_ptr<int> myFunction2() 
{ 
    boost::shared_ptr<int> x = new int; 

    *x = 4; 
    return x; 
} 

另一個選項是隻允許一個副本。如果對象很小(比如這個),那麼這也不算太壞,或者您可以安排在return語句中創建對象。如果在return語句中創建對象,編譯器通常會優化拷貝。

0

我會嘗試這樣的事:

int myFunction2b(int * px) 
{ 
    if(px) 
    { 
    *px = 4; 
    return 1; 
    } 

    // Choice 1: Assert or Report Error 
    // Choice 2: Allocate memory for x. Caller has to be written accordingly. 

    // My choice is 1 
    assert(0 && "Argument is NULL pointer"); 
    return 0; 

} 
-1

你詢問如何正確地返回一個指針。這是錯誤的問題,因爲你應該做的是使用智能指針而不是原始指針。使用scoped_ptr和shared_ptr的(在升壓和TR1可用)是很好的指針看(如herehere

如果需要原始指針的東西(例如,傳遞給C函數),該GET ()方法將提供它。

如果您必須創建原始指針,例如家庭作業,那麼你可以使用的malloc()(像你一樣)或一個函數中,希望你還記得解除分配內存(通過免費()分別刪除)或者,在一個稍微不太可能泄漏的習慣用語中,你可以用新的創建指針,將它傳遞給一個函數,並且在你完成之後用刪除。但是,再次使用智能指針。

1

你的第二個代碼片段是正確的。

爲了幫助避免內存泄漏,我讓編碼約定幫助我。

xxxCreate()將爲xxx分配內存並初始化它。 xxxDelete()將銷燬/損壞xxx並釋放它。

xxxInit()將初始化XXX(從未分配) xxxDestroy()會破壞/損壞XXX(永遠免費)

此外,我嘗試添加代碼,刪除/銷燬/免費,只要我添加創建/ init/malloc的代碼。這並不完美,但我發現它可以幫助我區分需要釋放的東西和不需要的東西,並減少我稍後忘記釋放某些東西的可能性。