我在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。
答案 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
来电中不要chunk
到fgets
,
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将会附加到分配中的任何垃圾字节。