2013-03-01 11 views
0

我在string.h標準庫中的strncat函數中看到一些奇怪的行爲,並希望能夠幫助您理解正在發生的事情。strncat看起來像它保存呼叫數據?


我的問題的關鍵是在功能我創建了名爲readLine與返回的文件作爲char *字符串的行末尾沒有新行終結的目的。這個功能看起來是這樣的:

char * readLine(FILE * fp) { 
    char * chunk = NULL; 
    char * line = NULL; 
    int count = 0; 

    // iterate through chunks of a line until we reach the end (or an error) 
    while (fgets((chunk = malloc(sizeof(char) * BUFFER_SIZE)), BUFFER_SIZE, fp) != NULL) { 

     // realloc on a null pointer works like malloc 
     line = realloc(line, ++count * BUFFER_SIZE * sizeof(char));  

     printf("chunk's contents: %s\n", chunk); 

     // does chunk contain the end of a line? 
     if(strchr(chunk, '\n') == NULL) { 
      // concatenate string parts and continue loop 
      strncat(line, chunk, strlen(chunk) + 1);   
      free(chunk); 

     } else { 
      // we want to return a \0 terminated string without the \n 
      // expected position of \n in chunk is ({length of chunk}-1) 
      chunk[(strlen(chunk) - 1)] = '\0'; 

      // concatenate string parts 
      strncat(line, chunk, strlen(chunk) + 1); 
      printf("readLine line: %s\n", line); 
      free(chunk); 

      break;   
     }   
    } 
    return line; 
} 

我把它稱爲主在一個循環中,看起來像這樣:

FILE * fp = NULL; 

if ((fp = fopen(FILE_PATH, "r")) != NULL) { 
    char * line = NULL; 

    while ((line = readLine(fp)) != NULL) { 
     printf("main line:  %s\n\n", line); 
     free(line); 
    } 

    fclose(fp); 
} 

現在的古怪行爲來到我的#define BUFFER_SIZE 1000定義。有了它設置一樣,我得到下面的輸出(這不是我想要的):

chunk's contents: I am on line 1 
readLine line: I am on line 1 
main line:  I am on line 1 

chunk's contents: Over here I am on line 2 
readLine line: I am on line 1Over here I am on line 2 
main line:  I am on line 1Over here I am on line 2 

chunk's contents: Line 3 here 
readLine line: I am on line 1Over here I am on line 2Line 3 here 
main line:  I am on line 1Over here I am on line 2Line 3 here 

chunk's contents: Look out for 4 
readLine line: I am on line 1Over here I am on line 2Line 3 hereLook out for 4 
main line:  I am on line 1Over here I am on line 2Line 3 hereLook out for 4 

chunk's contents: Johnny 5 alive! 
readLine line: I am on line 1Over here I am on line 2Line 3 hereLook out for 4Johnny 5 alive! 
main line:  I am on line 1Over here I am on line 2Line 3 hereLook out for 4Johnny 5 alive! 

,如果我這一定義更改爲類似#define BUFFER_SIZE 20,我得到我想要的輸出:

chunk's contents: I am on line 1 

readLine line: I am on line 1 
main line:  I am on line 1 

chunk's contents: Over here I am on l 
chunk's contents: ine 2 

readLine line: Over here I am on line 2 
main line:  Over here I am on line 2 

chunk's contents: Line 3 here 

readLine line: Line 3 here 
main line:  Line 3 here 

chunk's contents: Look out for 4 

readLine line: Look out for 4 
main line:  Look out for 4 

chunk's contents: Johnny 5 alive! 

readLine line: Johnny 5 alive! 
main line:  Johnny 5 alive! 

我想我已經縮小到strncat(line, chunk, strlen(chunk) + 1);線的問題。我不明白爲什麼當我的BUFFER_SIZE足夠高時,前面的line被包括在內。

+0

嘗試在使用它們之前將緩衝區的memset設置爲0。 – 2013-03-01 20:28:36

+0

請注意,您的'strncat'正在實現Schlemiel的Painter算法:http://www.joelonsoftware.com/articles/fog0000000319.html – ecatmur 2013-03-01 20:34:26

+1

請注意,使用'line = realloc(line,...'你將有一個不可恢復的內存泄漏應該realloc失敗,並返回NULL(你應該檢查)。從API的角度來看,'realloc'函數是可怕的,其中一個原因是它會導致寫入不正確的代碼,例如 – hlovdal 2013-03-02 00:49:59

回答

4
line = realloc(line, ++count * BUFFER_SIZE * sizeof(char)); 

不初始化分配的內存。因此,如果readLine中的第一個realloc可以讓您恢復之前調用中獲得的大量內存 - 這並非不可能,您可能仍然在其中。

無論如何,對於未初始化的內存,第一個strncat可能會調用未定義的行爲,因爲分配的內存中不需要有0字節。

在進入循環之前將緩衝區分配爲line,並將0寫入第一個字節。

另外,不要使用

line = realloc(line, ++count * BUFFER_SIZE * sizeof(char)); 

如果realloc失敗,則泄漏內存。你應該檢查的realloc返回值,

char *temp = realloc(line, ++count * BUFFER_SIZE * sizeof(char)); 
if (temp == NULL) { 
    // Oops 
} else { 
    line = temp; 
} 

而且不mallocchunkfgets電話,

while (fgets((chunk = malloc(sizeof(char) * BUFFER_SIZE)), BUFFER_SIZE, fp) != NULL) 

如果malloc失敗,太調用未定義的行爲。malloc並呼籲fgets前檢查,

while ((chunk = malloc(sizeof(char) * BUFFER_SIZE)) && fgets(chunk, BUFFER_SIZE, fp) != NULL) 
+0

好的答案,謝謝 在'realloc'成語中,如果'temp == NULL'是否適用於'free(line)'? 我真的很喜歡那個,而條件修改版 – 2013-03-01 20:49:47

+0

是的,那麼'line'仍然指向分配的內存,應該是'free'd。在'realloc'失敗時退出前做什麼樣的診斷和恢復/清理取決於。 – 2013-03-01 20:53:59

1

您可以realloc堅持和memset您的緩衝區之前到零,雖然。

1

你的問題是在這裏:

line = realloc(line, ++count * BUFFER_SIZE * sizeof(char));  

按照手冊頁realloc

"realloc(3) does not guarantee that the additional memory is also 
zero-filled." 

"If ptr is NULL, realloc() is identical to a call to malloc() for size bytes." 

所以你得到任何新的內存很可能被填補與非零字節,這意味着第一次被稱爲,它可能不會有0 f或第一個字節,這意味着strncat將會追加到分配中的任何垃圾字節。