0

strncat在标准库的函数中看到了一些奇怪的行为string.h,并且希望得到一些帮助来理解正在发生的事情。


我的问题的症结在于我创建的一个函数,该函数调用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);条线了。我不明白为什么line当 myBUFFER_SIZE足够高时会包含前面的 s 。

4

3 回答 3

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

不初始化分配的内存。因此,如果第一个reallocinreadLine将您在上一次调用中获得的内存块归还给您 - 并非不可能,您可能仍然有旧的内容。

无论如何,对于未初始化的内存,第一个strncat可能会调用未定义的行为,因为分配的内存中不需要 0 字节。

line在进入循环之前分配一个缓冲区,并将 a 写入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;
}

并且不要malloc在通话中chunkfgets

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)
于 2013-03-01T20:30:55.940 回答
1

不过,您可以坚持使用realloc并将缓冲区设置为零。

于 2013-03-01T20:34:12.993 回答
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,这意味着 strncat 将附加到任何垃圾字节在分配中。

于 2013-03-01T20:35:09.607 回答