strncat看起来像是在调用时保留数据吗?

时间:2013-03-01 20:24:07

标签: c strcat

我在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足够高时,我不明白为什么包含前面的BUFFER_SIZE s。

3 个答案:

答案 0 :(得分:4)

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

不会初始化已分配的内存。因此,如果realloc中的第一个readLine为您提供了上一次调用所获得的内存块 - 并非不可能,那么您可能还有旧内容。

无论如何,对于未初始化的内存,第一个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;
}

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)

答案 1 :(得分:1)

尽管如此,你可以坚持使用realloc并将缓冲区设为零。

答案 2 :(得分: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将会附加到分配中的任何垃圾字节。