試圖用指針參數申請動態內存
毛病出在函數GetMemory中。編譯器總是要爲函數的每個參數製作臨時副本,指針參數ip的副本是 _ip,編譯器使 _ip = ip。如果函數體內的程序修改了_ip的內容,就導致參數ip的內容作相應的修改。這就是指針可以用作輸出參數的原因。在本例中,_ip申請了新的內存,只是把_ip所指的內存地址改變了,但是ip絲毫未變。所以函數GetMemory並不能輸出任何東西。事實上,每執行一次GetMemory就會泄露一塊內存,因爲沒有用free釋放內存。
如果非得要用指針參數去申請內存,那麼應該改用"指向指針的指針",見如下示例:
C代碼
void GetMemory(char **p, int num)
{
*ip = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100); // 注意參數是 &str,而不是str
strcpy(str, "hello");
std::cout<< str << std::endl;
free(str);
}
void GetMemory(char **p, int num)
{
*ip = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100); // 注意參數是 &str,而不是str
strcpy(str, "hello");
std::cout<< str << std::endl;
free(str);
}
用指向指針的指針申請動態內存
當然,我們也可以用函數返回值來傳遞動態內存。這種方法更加簡單,見如下示例:
C代碼
char *GetMemory(int num)
{
char *ip = (char *)malloc(sizeof(char) * num);
return ip;
}
void Test(void)
{
char *str = NULL;
str = GetMemory(100);
strcpy(str, "hello");
std::cout<< str << std::endl;
free(str);
}
char *GetMemory(int num)
{
char *ip = (char *)malloc(sizeof(char) * num);
return ip;
}
void Test(void)
{
char *str = NULL;
str = GetMemory(100);
strcpy(str, "hello");
std::cout<< str << std::endl;
free(str);
}
用函數返回值來傳遞動態內存
用函數返回值來傳遞動態內存這種方法雖然好用,但是常常有人把return語句用錯了。這裏強調不要用return語句返回指向"棧內存"的指針,因爲該內存在函數結束時自動消亡,見如下示例:
C代碼
char *GetString(void)
{
char p[] = "hello world";
return p; // 編譯器將提出警告
}
void Test(void)
{
char *str = NULL;
str = GetString(); // str 的內容是垃圾
std::cout<< str << std::endl;
}
char *GetString(void)
{
char p[] = "hello world";
return p; // 編譯器將提出警告
}
void Test(void)
{
char *str = NULL;
str = GetString(); // str 的內容是垃圾
std::cout<< str << std::endl;
}
return語句返回指向"棧內存"的指針
最後,根據以上闡述,我們總結如下使用規則供大家參考:
【規則1】用malloc或new申請內存之後,應該立即檢查指針值是否爲NULL。防止使用指針值爲NULL的內存。
【規則2】不要忘記爲數組和動態內存賦初值。防止將未被初始化的內存作爲右值使用。
【規則3】避免數組或指針的下標越界,特別要當心發生"多1"或者"少1"操作。
【規則4】動態內存的申請與釋放必須配對,防止內存泄漏。
【規則5】用free或delete釋放了內存之後,立即將指針設置爲NULL,防止產生"野指針"。