我正在尝试将包含不同行的文本文件读入数组。该数组称为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)
它有什么问题?你有什么建议吗?我感谢你的帮助!
答案 0 :(得分:3)
几个问题和建议: -
在printLines
函数中,您始终打印,直到索引达到fileLen
。现在,如果fileLen
不等于i
或更高的值 - 它将尝试访问未初始化的值并将它们传递给printf
将是未定义的行为。 (chux评论和评论)
另外一件事 - 您检查字符串长度是否大于MAX_LINE_LEN
是错误的。你永远不能从中推断出“线太长”。读取fgets
返回值以获得所需的行为。 (它读取MAX_LINE_LEN-1
个字符,最后一个位置包含\0
。因此,您可以确定正在读取完整行的一种方法是找到\n
- 如果它在那里,那么正在读完整行。)
当fgets
返回NULL
时,您正在释放它们,但是再次将其设置为NULL
,以便在打印时可以选择性地打印有效的({1}}那些有NULL
)的人。你也必须考虑这个问题。是否从fgets
返回是由于错误情况 - 将使用feof()
和ferror()
进行检查。如果您使用的是POSIX
,则fgets
会设置errno
,如果它遇到除文件结束条件之外的某些故障。检查reference以获得更多想法。
在这种情况下,您分配的内存量大于您所需的内存量 - 但使用错误的type
的方式会产生问题。 char *lineArr = (char *) malloc(sizeof(char*) * (MAX_LINE_LEN + 1));
您要为MAX_LINE_LEN+1
char*
分配空间 - 应该是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));
答案 1 :(得分:1)
由于lineArr是一个字符数组而不是字符串数组,因此应该将sizeof(char)作为参数而不是sizeof(char *)
以下是您发布的代码的工作版本。我用calloc替换了malloc(个人偏好,否则两者都没问题)。并用while(fgets(lineArr,MAX_LINE_LEN,file))替换while(1),以便循环在文件结束后立即结束。
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;