下面這道面試題是有關指針、動態內存分配的相關內容,感覺比較經典,記錄下來大家共享。
問題:
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。