Valgrind 使用未初始化的值和无效读取
Valgrind Use of unitialized value and Invalid read
我正在尝试将包含不同行的文本文件读入数组。该数组称为fileArr
,它是一个指针数组,每个索引指向一个lineArr
字符串。
正在打开文件并检查文件长度。但是当我开始将文件读入数组时,就会出现问题。这是我的代码:
int main()
{
fseek(file, 0, SEEK_END); // seek to end of file
int fileLen = ftell(file); // get current file pointer
fseek(file, 0, SEEK_SET); // seek back to beginning of file
printf("file length = %d\n", fileLen);
char **fileArr = malloc(sizeof(char*) * fileLen);
char *lineArr = (char *) malloc(sizeof(char*) * (MAX_LINE_LEN + 1));
int i = 0;
while(1) {
fileArr[i] = malloc(sizeof(char*) * (MAX_LINE_LEN + 1)); //This is line 73
// Read from the file
if(fgets(lineArr, MAX_LINE_LEN, file) != NULL) {
// Check if line is too long
if(strlen(lineArr) > MAX_LINE_LEN) {
fprintf(stderr, "Line too long");
exit(1); // exit with return code 1
}
// If not, write content in one line to array
strcpy(fileArr[i], lineArr);
}
else { // If reach to the end of file
// Free the fileArr at index i
free(fileArr[i]);
break;
}
i++;
}
// Then print out the array
printLines(fileArr, fileLen); // This is line 91
// Free memory
free(lineArr);
free(fileArr);
return 0;
}
/** Method to print out array **/
void printLines (char *ptArray[], size_t count)
{
for (size_t i = 0; i < count; i++) {
printf("%s\n", ptArray[i]); // This is line 100
}
}
文件已打印出来,但出现分段错误。然后 valgrind 打印出这个巨大的可怕消息(包含此代码的文件的名称是 textsort.c):
==13032== Invalid read of size 1
==13032== at 0x4C30F62: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032== by 0x4EA969B: puts (ioputs.c:35)
==13032== by 0x400DB0: printLines (textsort3.c:100)
==13032== by 0x400D54: main (textsort3.c:91)
==13032== Address 0x5207180 is 0 bytes inside a block of size 1,032 free'd
==13032== at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032== by 0x400D28: main (textsort3.c:83)
==13032== Block was alloc'd at
==13032== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032== by 0x400C88: main (textsort3.c:73)
==13032==
==13032== Use of uninitialised value of size 8
==13032== at 0x4C30F62: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032== by 0x4EA969B: puts (ioputs.c:35)
==13032== by 0x400DB0: printLines (textsort3.c:100)
==13032== by 0x400D54: main (textsort3.c:91)
==13032==
==13032==
==13032== Process terminating with default action of signal 11 (SIGSEGV)
==13032== Access not within mapped region at address 0x0
==13032== at 0x4C30F62: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032== by 0x4EA969B: puts (ioputs.c:35)
==13032== by 0x400DB0: printLines (textsort3.c:100)
==13032== by 0x400D54: main (textsort3.c:91)
==13032== If you believe this happened as a result of a stack
==13032== overflow in your program's main thread (unlikely but
==13032== possible), you can try to increase the size of the
==13032== main thread stack using the --main-stacksize= flag.
==13032== The main thread stack size used in this run was 8388608.
==13032==
==13032== HEAP SUMMARY:
==13032== in use at exit: 6,304 bytes in 6 blocks
==13032== total heap usage: 10 allocs, 4 frees, 13,008 bytes allocated
==13032==
==13032== LEAK SUMMARY:
==13032== definitely lost: 0 bytes in 0 blocks
==13032== indirectly lost: 0 bytes in 0 blocks
==13032== possibly lost: 0 bytes in 0 blocks
==13032== still reachable: 6,304 bytes in 6 blocks
==13032== suppressed: 0 bytes in 0 blocks
==13032== Reachable blocks (those to which a pointer was found) are not shown.
==13032== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==13032==
==13032== For counts of detected and suppressed errors, rerun with: -v
==13032== Use --track-origins=yes to see where uninitialised values come from
==13032== ERROR SUMMARY: 3 errors from 2 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
它的问题是什么?你有什么建议吗?感谢您的帮助!
几个问题和建议:-
在 printLines
函数中,您总是打印直到索引达到 fileLen
。现在,如果 fileLen
不等于 i
或更高的值 - 它将尝试访问未初始化的值并将它们传递给 printf
将是未定义的行为。 (chux评论和alk评论)
还有一件事——你检查的字符串长度大于 MAX_LINE_LEN
是错误的。你永远不能从中推断出 "Line is too Long"。读取 fgets
return 值以获得所需的行为。 (它读取 MAX_LINE_LEN-1
个字符,最后一个位置包含 [=19=]
。因此,您可以确定正在读取完整行的一种方法是找到 \n
- 如果它在那里,那么正在读取整行)。
此外,当 fgets
returns NULL
你正在释放它们,但你又没有将其设置为 NULL
,这样当你打印时你可以选择打印有效的(不是那些有 NULL
的)。你也得考虑这个。来自 fgets
的 return 是否是由于错误条件引起的 - 将使用 feof()
和 ferror()
检查。如果您使用 POSIX
一个,那么 fgets
设置 errno
如果遇到文件结束条件以外的其他故障。查看 reference 了解更多信息。
在这种情况下,您分配的内存量比您需要的要大 - 但您使用错误 type
的方式会产生问题。 char *lineArr = (char *) malloc(sizeof(char*) * (MAX_LINE_LEN + 1));
您正在为 MAX_LINE_LEN+1
char*
-s 分配 space 它应该是 char
-s。那就是你要存储在 lineArr
指向的内存中的内容。 fileArr[i]
.
中的其他分配也是如此
更正后(请注意,转换已删除,转换是隐式的)sizeof
char
始终是 1
。所以我们也可以按照评论中的方式来做。
char *lineArr = malloc(sizeof(char) * (MAX_LINE_LEN + 1));//malloc(MAX_LINE_LEN + 1)
...
fileArr[i] = malloc(sizeof(char) * (MAX_LINE_LEN + 1));
由于 lineArr 是一个字符数组而不是字符串数组,它应该被赋予 sizeof(char) 作为参数而不是 sizeof(char*)
以下是您发布的代码的工作版本。我已经用 calloc 替换了 malloc(个人喜好,否则两者都可以)。并将 while(1) 替换为 while(fgets(lineArr, MAX_LINE_LEN, file)) 以便循环在文件结束后立即结束。
char **fileArr = (char**)calloc(sizeof(char*), fileLen);
char *lineArr = (char *)calloc(sizeof(char), (MAX_LINE_LEN + 1));
int i = 0;
while (fgets(lineArr, MAX_LINE_LEN, file))
{
fileArr[i] = (char*)calloc(sizeof(char), (MAX_LINE_LEN + 1)); //This is line 73
if (strlen(lineArr) > MAX_LINE_LEN)
{
fprintf(stderr, "Line too long");
exit(1);
}
strcpy(fileArr[i], lineArr);
i++;
}
printLines(fileArr, fileLen);
free(lineArr);
free(fileArr);
getch();
return 0;
我正在尝试将包含不同行的文本文件读入数组。该数组称为fileArr
,它是一个指针数组,每个索引指向一个lineArr
字符串。
正在打开文件并检查文件长度。但是当我开始将文件读入数组时,就会出现问题。这是我的代码:
int main()
{
fseek(file, 0, SEEK_END); // seek to end of file
int fileLen = ftell(file); // get current file pointer
fseek(file, 0, SEEK_SET); // seek back to beginning of file
printf("file length = %d\n", fileLen);
char **fileArr = malloc(sizeof(char*) * fileLen);
char *lineArr = (char *) malloc(sizeof(char*) * (MAX_LINE_LEN + 1));
int i = 0;
while(1) {
fileArr[i] = malloc(sizeof(char*) * (MAX_LINE_LEN + 1)); //This is line 73
// Read from the file
if(fgets(lineArr, MAX_LINE_LEN, file) != NULL) {
// Check if line is too long
if(strlen(lineArr) > MAX_LINE_LEN) {
fprintf(stderr, "Line too long");
exit(1); // exit with return code 1
}
// If not, write content in one line to array
strcpy(fileArr[i], lineArr);
}
else { // If reach to the end of file
// Free the fileArr at index i
free(fileArr[i]);
break;
}
i++;
}
// Then print out the array
printLines(fileArr, fileLen); // This is line 91
// Free memory
free(lineArr);
free(fileArr);
return 0;
}
/** Method to print out array **/
void printLines (char *ptArray[], size_t count)
{
for (size_t i = 0; i < count; i++) {
printf("%s\n", ptArray[i]); // This is line 100
}
}
文件已打印出来,但出现分段错误。然后 valgrind 打印出这个巨大的可怕消息(包含此代码的文件的名称是 textsort.c):
==13032== Invalid read of size 1
==13032== at 0x4C30F62: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032== by 0x4EA969B: puts (ioputs.c:35)
==13032== by 0x400DB0: printLines (textsort3.c:100)
==13032== by 0x400D54: main (textsort3.c:91)
==13032== Address 0x5207180 is 0 bytes inside a block of size 1,032 free'd
==13032== at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032== by 0x400D28: main (textsort3.c:83)
==13032== Block was alloc'd at
==13032== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032== by 0x400C88: main (textsort3.c:73)
==13032==
==13032== Use of uninitialised value of size 8
==13032== at 0x4C30F62: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032== by 0x4EA969B: puts (ioputs.c:35)
==13032== by 0x400DB0: printLines (textsort3.c:100)
==13032== by 0x400D54: main (textsort3.c:91)
==13032==
==13032==
==13032== Process terminating with default action of signal 11 (SIGSEGV)
==13032== Access not within mapped region at address 0x0
==13032== at 0x4C30F62: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032== by 0x4EA969B: puts (ioputs.c:35)
==13032== by 0x400DB0: printLines (textsort3.c:100)
==13032== by 0x400D54: main (textsort3.c:91)
==13032== If you believe this happened as a result of a stack
==13032== overflow in your program's main thread (unlikely but
==13032== possible), you can try to increase the size of the
==13032== main thread stack using the --main-stacksize= flag.
==13032== The main thread stack size used in this run was 8388608.
==13032==
==13032== HEAP SUMMARY:
==13032== in use at exit: 6,304 bytes in 6 blocks
==13032== total heap usage: 10 allocs, 4 frees, 13,008 bytes allocated
==13032==
==13032== LEAK SUMMARY:
==13032== definitely lost: 0 bytes in 0 blocks
==13032== indirectly lost: 0 bytes in 0 blocks
==13032== possibly lost: 0 bytes in 0 blocks
==13032== still reachable: 6,304 bytes in 6 blocks
==13032== suppressed: 0 bytes in 0 blocks
==13032== Reachable blocks (those to which a pointer was found) are not shown.
==13032== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==13032==
==13032== For counts of detected and suppressed errors, rerun with: -v
==13032== Use --track-origins=yes to see where uninitialised values come from
==13032== ERROR SUMMARY: 3 errors from 2 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
它的问题是什么?你有什么建议吗?感谢您的帮助!
几个问题和建议:-
在
printLines
函数中,您总是打印直到索引达到fileLen
。现在,如果fileLen
不等于i
或更高的值 - 它将尝试访问未初始化的值并将它们传递给printf
将是未定义的行为。 (chux评论和alk评论)还有一件事——你检查的字符串长度大于
MAX_LINE_LEN
是错误的。你永远不能从中推断出 "Line is too Long"。读取fgets
return 值以获得所需的行为。 (它读取MAX_LINE_LEN-1
个字符,最后一个位置包含[=19=]
。因此,您可以确定正在读取完整行的一种方法是找到\n
- 如果它在那里,那么正在读取整行)。此外,当
fgets
returnsNULL
你正在释放它们,但你又没有将其设置为NULL
,这样当你打印时你可以选择打印有效的(不是那些有NULL
的)。你也得考虑这个。来自fgets
的 return 是否是由于错误条件引起的 - 将使用feof()
和ferror()
检查。如果您使用POSIX
一个,那么fgets
设置errno
如果遇到文件结束条件以外的其他故障。查看 reference 了解更多信息。在这种情况下,您分配的内存量比您需要的要大 - 但您使用错误
中的其他分配也是如此type
的方式会产生问题。char *lineArr = (char *) malloc(sizeof(char*) * (MAX_LINE_LEN + 1));
您正在为MAX_LINE_LEN+1
char*
-s 分配 space 它应该是char
-s。那就是你要存储在lineArr
指向的内存中的内容。fileArr[i]
.更正后(请注意,转换已删除,转换是隐式的)
sizeof
char
始终是1
。所以我们也可以按照评论中的方式来做。char *lineArr = malloc(sizeof(char) * (MAX_LINE_LEN + 1));//malloc(MAX_LINE_LEN + 1) ... fileArr[i] = malloc(sizeof(char) * (MAX_LINE_LEN + 1));
由于 lineArr 是一个字符数组而不是字符串数组,它应该被赋予 sizeof(char) 作为参数而不是 sizeof(char*)
以下是您发布的代码的工作版本。我已经用 calloc 替换了 malloc(个人喜好,否则两者都可以)。并将 while(1) 替换为 while(fgets(lineArr, MAX_LINE_LEN, file)) 以便循环在文件结束后立即结束。
char **fileArr = (char**)calloc(sizeof(char*), fileLen);
char *lineArr = (char *)calloc(sizeof(char), (MAX_LINE_LEN + 1));
int i = 0;
while (fgets(lineArr, MAX_LINE_LEN, file))
{
fileArr[i] = (char*)calloc(sizeof(char), (MAX_LINE_LEN + 1)); //This is line 73
if (strlen(lineArr) > MAX_LINE_LEN)
{
fprintf(stderr, "Line too long");
exit(1);
}
strcpy(fileArr[i], lineArr);
i++;
}
printLines(fileArr, fileLen);
free(lineArr);
free(fileArr);
getch();
return 0;