您可能需要在編譯器中打開更多警告或獲得更好的編譯器。當我編譯你的代碼,我得到了警告:
mem.c: In function ‘main’:
mem.c:12: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’
mem.c:12: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’
mem.c: At top level:
mem.c:7: warning: unused parameter ‘argc’
mem.c:7: warning: unused parameter ‘argv’
在MacOS
我不明白爲什麼重複上線12的警告,但兩者GCC 4.2和4.6.1 X 10.7給它的兩倍。當您不使用命令行參數時,關於argc
和argv
的警告是使用int main(void)
的好理由;他們不是一個大問題。
關於%s
和int
和char *
的警告(真的是錯誤)足以說明您的崩潰 - 但這不是代碼的唯一問題。事實上,也有:
- 一個初期的內存泄漏,
- 實際泄漏,
- 你在哪裏(意外)依賴未定義行爲的地方,
- 一個地方,你」重新依賴實現定義的行爲,這可能不符合您的期望。
您的代碼註釋:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int f1(char **str_);
int main(int argc, char **argv)
{
char *str = NULL;
f1(&str);
printf("str : %s\n", *str);
str = realloc(str, (size_t) 0); /* (3) Undefined behaviour */
assert(str == NULL); /* (4) Implementation-defined behaviour */
return 0;
}
int f1(char **str_)
{
if ((*str_ = realloc(*str_, sizeof(char) * 12)) == NULL) /* (1) Incipient leak */
{
fprintf(stderr,"realloc() failed\n");
exit(3);
}
(*str_) = "hello there"; /* (2) Actual leak */
return 0;
}
按編號順序在討論這些:
如果內存重新分配失敗的初期泄漏發生。 *str
是存儲指向已分配內存的指針的前一個值的唯一位置,如果realloc()
失敗,則返回0(空指針),但不釋放舊內存,但不再有指向舊內存的指針。
的修復:
char *new_mem = realloc(*str, 12);
if (new_mem == 0)
...error handling...
*str = new_mem;
拇指的規則:不要realloc()
的返回值分配到這是它的第一個參數變量。
由於您將指針指向指向新分配內存的指針的字符串常量,因此出現實際泄漏。最簡單的解決方法是使用strcpy()
,但您需要在此時添加#include <string.h>
。你會還,通常情況下,要確保你分配你要複製字符串只是足夠的空間,從而導致:
char const hello[] = "hello there";
char *new_mem = realloc(str, sizeof(hello));
//char *new_mem = realloc(str, strlen(hello)+1);
if (new_mem == 0)
...handle error...
*str = new_mem;
strcpy(new_mem, hello);
在這個例子中,我可以使用sizeof(hello)
因爲字符串的長度包括空終止,並且因爲實際的數組定義在範圍之內。如果要複製的字符串作爲指針傳遞到函數中,則使用strlen(hello)+1
的替代方法是正確的(並且使用sizeof()
不正確),即使它需要運行時長度計算而不是編譯時間計算(如圖所示)。
未定義的行爲是由於(2)處的內存泄漏引起的。您嘗試realloc()
一個字符串常量,而不是由realloc()
返回的指針。這導致了未定義的行爲;它可能會崩潰(因爲realloc()
試圖將控制信息寫入只讀存儲器),或者它可能會簡化內存系統,導致一段時間後崩潰。
對此的修正是對項目(2)的修正。
實現定義的行爲的出現是因爲C標準說:
§7.20.3內存管理功能¶1:[...]如果空間的請求的大小爲零,行爲是實現定義的: 返回空指針,或者行爲就好像大小是非零值,但返回的指針不能用於訪問對象。
您斷言您的實現將選擇第一個選項,但它可能會選擇第二個選項。解決辦法是刪除無根據的斷言。
所以,這是共有5個問題的代碼,其中只有一個編譯器可能會幫助你。
很好,詳細的解釋各種問題。 –
非常好,先生。非常感謝你,我學到了很多東西。 – pharaoh