函數不能傳遞動態內存

下面這道面試題是有關指針、動態內存分配的相關內容,感覺比較經典,記錄下來大家共享。

問題

What will happen after running the "Test"?

#include <iostream>
using namespace std;
void GetMemory(char* p, int num)
{
	p = (char*)malloc(sizeof(char*)num);
};

int main()
{
	char* str = NULL;
	GetMemory(str, 100);
	strcpy(str, "hello");
	return 0;
}

分析

在上述例子中,毛病出在GetMemory中。函數中的形參*p實際上是主函數中str的一個副本,編譯器總是要爲函數的每個參數製作臨時副本。在本例中,p申請了新的內存,只是把p所指的內存地址改變了,但是str絲毫未變。因爲函數GetMemory沒有返回值,因此str並不指向p所申請的那段內存,所以函數GetMemory並不能輸出任何東西。事實上,每執行一次GetMemory函數就會申請一塊內存,但申請的內存一直被獨佔,最終造成內存泄漏。

解決

如果一定要用指針參數去申請內存,那麼應該採用指向指針的指針,傳遞str的地址給函數GetMemory。代碼如下:

#include <iostream>
using namespace std;
void GetMemory(char** p, int num)
{
	*p = (char*)malloc(sizeof(char*)num);
};

int main()
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	cout << *str << endl;
	cout << str << endl;
	cout << &str << endl;
	return 0;
}
這樣的話,程序就可以成功運行。字符串是一個比較特殊的例子,我們分別打印*str、str、&str可以發現,結果分別是h、hello、0*22f7c。str就是字符串的值,*str是字符串的首字符,&str是字符串的地址。

由於“指向指針的指針”這個概念不容易理解,我們可以用函數返回值來傳遞動態內存。這種方法更加簡單,方法如下:

#include <iostream>
using namespace std;
char* GetMemory(char* p, int num)
{
	*p = (char*)malloc(sizeof(char*)num);
	return p;
};

int main()
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	
	return 0;
}

我們可以對這道題推而廣之,看一下整型變量是如何傳值的,代碼如下:

#include <iostream>
using namespace std;
void GetMemory2(int* z)
{
	*z = 5;
};

int main()
{
	int v;
	GetMemory2(&v)
	cout << v << endl;
	
	return 0;
}
GetMemory把v的地址傳了進來,*z是地址裏的值,是v的副本。通過直接修改地址裏的值,不需要有返回值,於是也把v給修改了,因爲v所指向的地址的值發生了改變。


所以該題的答案是:程序崩潰。因爲函數並不能傳遞動態分配的內存,主函數中的str一直都是NULL。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章