2013-03-01 6 views
0

string.h 표준 라이브러리의 strncat 기능에서 이상한 동작을보고 있으며, 어떤 일이 벌어지고 있는지 이해하는 데 도움이되고 싶습니다.strncat은 호출간에 데이터를 보존하는 것처럼 보입니까?


내 문제의 핵심은 내가 뒤에 줄 바꿈 종료하지 않고 char * 문자열로 파일의 라인을 반환 목적으로 readLine라고 만든 함수입니다. 이 함수는 다음과 같습니다

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

버퍼를 사용하기 전에 버퍼를 0으로 설정하십시오. –

+0

'strncat'은 Painter의 알고리즘 인 Schlemiel을 구현하고 있습니다 : http://www.joelonsoftware.com/articles/fog0000000319.html – ecatmur

+1

'line = realloc (line, ...'으로 복구 할 수없는 메모리를 갖게 될 것입니다 누수가 realloc 실패하고 NULL을 반환해야합니다 (당신이 확인해야합니다.)'realloc' 함수는 API의 관점에서 볼 때 끔찍한데, 그 이유는 다음과 같습니다. – hlovdal

답변

4

은 할당 된 메모리를 초기화하지 않습니다. 따라서 readLine에있는 첫 번째 realloc이 이전 호출에서 얻은 메모리 덩어리를 돌려 준다면 불가능하지는 않습니다. 이전 내용이 여전히 남아있을 수 있습니다.

어쨌든 초기화되지 않은 메모리의 경우 첫 번째 strncat은 할당 된 메모리에 0 바이트가 필요하지 않으므로 정의되지 않은 동작을 호출 할 수 있습니다.

루프를 입력하기 전에 line에 버퍼를 할당하고 0을 첫 번째 바이트에 씁니다. realloc가 실패하면 또한

, 당신은 메모리 누수가,

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

를 사용하지 마십시오. malloc이 실패하면 당신도 정의되지 않은 동작을 호출하는,

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

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

realloc의 반환 값을 확인하고 fgets 호출하지 mallocchunk에 수행해야합니다.mallocfgets를 호출하기 전에 확인

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

감사합니다. 'realloc' 관용구에서 'temp == NULL'이라면'free (line)'하는 것이 적절할까요? 컨디션 리비젼도 너무 좋아요. –

+0

예,'line'은 여전히 ​​할당 된 메모리를 가리 킵니다. 'realloc'이 실패 할 때 종료하기 전에 어떤 종류의 진단 및 복구/정리가 필요한지에 따라 다릅니다. –

1

당신은 realloc을 고수하고 있지만, 이전에 제로로 당신의 버퍼를 memset 함수 수 있습니다. realloc에 대한 man 페이지에 따르면

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

:

1

귀하의 문제는 여기에있다

"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이 아닌 바이트, 즉 그것이 처음 호출 될 때, 아마도 0 f를 가지지 않을 것입니다. 또는 첫 번째 바이트는 strncat이 할당 된 모든 정크 바이트에 추가된다는 것을 의미합니다.