[英]return pointer to data declared in function
我知道這不起作用,因為變量x在函數返回時被銷毀:
int* myFunction()
{
int x = 4; return &x;
}
那么如何正確地返回指向我在函數中創建的東西的指針,以及我需要注意什么? 如何避免內存泄漏?
我也用過malloc:
int* myFunction2()
{
int* x = (int*)malloc(sizeof int); *x = 4; return x;
}
你是如何正確地做到這一點的 - 在C和C ++中?
對於C ++,您可以使用智能指針來強制執行所有權轉移。 auto_ptr
或boost::shared_ptr
是不錯的選擇。
你的第二種方法是正確的。 您只需要清楚地記錄調用者“擁有”結果指針,並負責釋放它。
由於這種額外的復雜性,很少為像“int”這樣的“小”類型執行此操作,盡管我假設您在這里僅使用了一個int作為示例。
有些人還希望將指向已分配對象的指針作為參數,而不是在內部分配對象。 這使得更清楚的是調用者負責釋放對象(因為他們首先分配它),但是使調用站點更加冗長,所以這是一個權衡。
對於C ++,在許多情況下,只需按值返回。 即使在較大物體的情況下, RVO也會經常避免不必要的復制。
一種可能性是將函數傳遞給指針:
void computeFoo(int *dest) {
*dest = 4;
}
這很好,因為你可以使用這樣一個自動變量的函數:
int foo;
computeFoo(&foo);
使用這種方法,您還可以將內存管理保留在代碼的相同部分,即。 你不能錯過malloc只是因為它發生在函數內的某個地方:
// Compare this:
int *foo = malloc(…);
computeFoo(foo);
free(foo);
// With the following:
int *foo = computeFoo();
free(foo);
在第二種情況下,你更容易忘記免費,因為你沒有看到malloc。 這通常至少部分通過約定來解決,例如:“如果函數名稱以XY開頭,則表示您擁有它返回的數據。”
返回指向“function”變量的指針的一個有趣的角落情況是聲明變量static:
int* computeFoo() {
static int foo = 4;
return &foo;
}
當然這對於正常編程來說是邪惡的,但它有朝一日可能會派上用場。
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不同,它具有真正的移動語義。
在C ++中,您應該使用new
:
int *myFunction() { int blah = 4; return new int(blah); }
要擺脫它,使用刪除:
int main(void) { int *myInt = myFunction(); // do stuff delete myInt; }
請注意,我在使用new
調用int的復制構造函數,因此將值“4”復制到堆內存中。 獲取指向堆棧上某些東西的指針的唯一方法是通過正確調用new
將其復制到堆上。
編輯:如另一個答案中所述,您還需要記錄稍后調用者需要釋放指針。 否則您可能會發生內存泄漏。
還有另一種方法 - 聲明x
靜態。 在這種情況下,它將位於數據段中,而不是堆棧中,因此在程序運行時期間它是可用的(並且是持久的)。
int *myFunction(void)
{
static int x = 4;
return &x;
}
請注意,只有在第一次調用myFunction
才會執行賦值x=4
:
int *foo = myFunction(); // foo is 4
*foo = 10; // foo is 10
*foo = myFunction(); // foo is 10
NB! 使用函數范圍靜態變量不是踏板安全技術。
您的第二個代碼段是正確的。
為了幫助避免內存泄漏,我讓編碼約定幫助我。
xxxCreate()將為xxx分配內存並初始化它。 xxxDelete()將破壞/損壞xxx並釋放它。
xxxInit()將初始化xxx(永不分配)xxxDestroy()將破壞/損壞xxx(永不免費)
另外,一旦我將代碼添加到create / init / malloc,我就會嘗試添加代碼來刪除/銷毀/釋放。 它並不完美,但我發現它可以幫助我區分需要釋放的物品和不需要物品的物品,以及減少我以后忘記釋放物品的可能性。
Boost或TR1共享指針通常是要走的路。 它避免了復制開銷,並為您提供半自動刪除。 所以你的功能應該是這樣的:
boost::shared_ptr<int> myFunction2()
{
boost::shared_ptr<int> x = new int;
*x = 4;
return x;
}
另一種選擇就是允許復制。 如果對象很小(比如這個),那也不錯,或者你可以安排在return語句中創建對象。 如果在return語句中創建對象,編譯器通常會優化副本。
我會嘗試這樣的事情:
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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.