2011-11-10 185 views
0

我想了解如何指針指針工作,我出來這個例子,它編譯好。但是,當它執行時,我會遇到分段錯誤。 PS:我不想f1()返回char *指針在C指針

#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); 
    assert(str == NULL); 

    return 0; 
} 

int f1(char **str_) 
{ 
    if ((*str_ = realloc(*str_, sizeof(char) * 12)) == NULL) 
    { 
     fprintf(stderr,"realloc() failed\n"); 
     exit(3); 
    } 

    (*str_) = "hello there"; 

    return 0; 
} 
  • 什麼是錯的代碼?

回答

9

您可能需要在編譯器中打開更多警告或獲得更好的編譯器。當我編譯你的代碼,我得到了警告:

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給它的兩倍。當您不使用命令行參數時,關於argcargv的警告是使用int main(void)的好理由;他們不是一個大問題。

關於%sintchar *的警告(真的是錯誤)足以說明您的崩潰 - 但這不是代碼的唯一問題。事實上,也有:

  1. 一個初期的內存泄漏,
  2. 實際泄漏,
  3. 你在哪裏(意外)依賴未定義行爲的地方,
  4. 一個地方,你」重新依賴實現定義的行爲,這可能不符合您的期望。

您的代碼註釋:

#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; 
} 

按編號順序在討論這些:

  1. 如果內存重新分配失敗的初期泄漏發生。 *str是存儲指向已分配內存的指針的前一個值的唯一位置,如果realloc()失敗,則返回0(空指針),但不釋放舊內存,但不再有指向舊內存的指針。

    的修復:

    char *new_mem = realloc(*str, 12); 
    if (new_mem == 0) 
        ...error handling... 
    *str = new_mem; 
    

    拇指的規則:不要realloc()的返回值分配到這是它的第一個參數變量。

  2. 由於您將指針指向指向新分配內存的指針的字符串常量,因此出現實際泄漏。最簡單的解決方法是使用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()不正確),即使它需要運行時長度計算而不是編譯時間計算(如圖所示)。

  3. 未定義的行爲是由於(2)處的內存泄漏引起的。您嘗試realloc()一個字符串常量,而不是由realloc()返回的指針。這導致了未定義的行爲;它可能會崩潰(因爲realloc()試圖將控制信息寫入只讀存儲器),或者它可能會簡化內存系統,導致一段時間後崩潰。

    對此的修正是對項目(2)的修正。

  4. 實現定義的行爲的出現是因爲C標準說:

    §7.20.3內存管理功能¶1:[...]如果空間的請求的大小爲零,行爲是實現定義的: 返回空指針,或者行爲就好像大小是非零值,但返回的指針不能用於訪問對象。

    您斷言您的實現將選擇第一個選項,但它可能會選擇第二個選項。解決辦法是刪除無根據的斷言。

所以,這是共有5個問題的代碼,其中只有一個編譯器可能會幫助你。

+1

很好,詳細的解釋各種問題。 –

+0

非常好,先生。非常感謝你,我學到了很多東西。 – pharaoh

6

main()的第一行將變量str設置爲NULL,並將指針傳遞給f1

f1運行完好。 f1的結果是main裏面的變量str現在是一個指向存儲該字符串(文字)"hello there"的空間的指針。

您的下一行,printf,段錯誤。爲什麼?因爲您正嘗試以字符串形式(格式說明符%s)打印*str(注意星號在這裏!!)。什麼是*str作爲字符串解釋?無論「地址」用「hell」表示(至少在32位機器上)。懷疑這個地址在你的進程空間。

經典段錯誤。

嘗試通過str而不是*strprintf

,將工作,看到http://codepad.org/Mh00txen

還有其他問題的代碼,例如f1中的12個字符的realloc除了導致內存泄漏之外什麼都不做,因爲您立即重新指定指向字符串文本的指針,但這不是導致段錯誤的原因。