c - 从命令行读取最后 10 行的程序中的分段错误
问题描述
我目前正在尝试创建一个程序,该程序打印通过命令行传入的另一个文本程序的最后 10 行。函数 read() 应该从文本文件中读取一行,然后返回字符串或 NULL,而 main 函数应该继续分配 read() 的输出,直到分配 NULL。
我经历了这个程序的许多不同版本,但它总是导致分段错误。我将如何开始调试分段错误?
我已经包含了下面的程序。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *read(){
char *line = (char *) malloc (80 * sizeof(char));
fgets(line,80,stdin);
if (line != NULL) {
line[strlen(line)-1] = '\0';
return line;
}
else {
free(line);
return NULL;
}
}
int main()
{
int i=0,j,k,l;
char **arr = (char **) malloc (100 * sizeof(char *));
for (k = 0; k < 100; k++) {
arr[k] = malloc (80 * sizeof(char));
}
while(1){
strcpy(arr[i],read());
if (arr[i]=NULL) break;
i++;
//printf("%s", arr[i]); //DEBUG
}
for (l = 0; l < 100; l++){
free(arr[l]);
}
free(arr);
for (j = i-11; j < i; j++) {
printf("%s\n", arr[j]);
}
printf("\n");
return 0;
}
解决方案
主要是
while(1){
strcpy(arr[i],read());
if (arr[i]=NULL) break;
你永远不会退出循环,因为arr[i]=NULL
没有理由从read中判断为真(因为read中的错误),所以你用未定义的行为写出arr(你的崩溃)
您没有很好地管理 read 中的文件结尾:
fgets(line,80,stdin);
if (line != NULL) {
你需要检查fgets的结果,而不是如果line是 NULL 除了检查malloc成功
这不是line[strlen(line)-1] = '\0';
没用的,因为fgets放了一个最终的空字符,幸运的是,否则strlen如何工作?
所以阅读可以是:
char *read(){
char *line = (char *) malloc (80 * sizeof(char));
if (line != NULL) {
if (fgets(line,80,stdin) == NULL) {
free(line);
return NULL;
}
}
return line;
}
因为 read 可以返回一个 NULL 你需要改变
strcpy(arr[i],read());
这也是为什么你在 read 分配的同时预先分配行的原因?目前您在读取时释放了每次分配的内存
将循环替换为
while((arr[i] == read()) != NULL)
//printf("%s", arr[i]); //DEBUG
i += 1;
}
并删除行的预分配
另一个问题是当文件超过 100 行时(或者可能是一些行在太长之前被剪切),在这种情况下,您会写出arr。其实不需要保存那么多行,最多只需要保存10行
一份提案 :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *read() {
char line[80];
if (fgets(line, 80, stdin) == NULL)
return NULL;
return strdup(line);
}
int main()
{
char * arr[10] = { NULL };
char * p;
int i = 0, n = 0;
while((p = read()) != NULL) {
n += 1;
free(arr[i]);
arr[i] = p;
if (++i == 10)
i = 0;
}
/* get older line index and line count to print */
if (n <= 10)
i = 0;
else
n = 10;
while (n-- != 0) {
puts(arr[i]);
free(arr[i]);
if (++i == 10)
i = 0;
}
return 0;
}
Compilation and execution :
pi@raspberrypi:/tmp/d $ gcc -g -pedantic -Wextra q.c
pi@raspberrypi:/tmp/d $ ./a.out < q.c
n = 10;
while (n-- != 0) {
puts(arr[i]);
if (++i == 10)
i = 0;
}
return 0;
}
注意我没有删除 \n 是读取行并且我使用puts所以打印了空行,而读取行没有被剪切,因为太长了
如果文件短于 10 行:
pi@raspberrypi:/tmp/d $ tail -n 5 q.c | ./a.out
i = 0;
}
return 0;
}
在valgrind下:
pi@raspberrypi:/tmp/d $ valgrind ./a.out < q.c
==18496== Memcheck, a memory error detector
==18496== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18496== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==18496== Command: ./a.out
==18496==
while (n-- != 0) {
puts(arr[i]);
free(arr[i]);
if (++i == 10)
i = 0;
}
return 0;
}
==18496==
==18496== HEAP SUMMARY:
==18496== in use at exit: 0 bytes in 0 blocks
==18496== total heap usage: 43 allocs, 43 frees, 5,699 bytes allocated
==18496==
==18496== All heap blocks were freed -- no leaks are possible
==18496==
==18496== For counts of detected and suppressed errors, rerun with: -v
==18496== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)
推荐阅读
- sql-server - 如何从查询中获取列列表?
- oracle - 如何修复“不允许新或旧引用”?
- python - 从嵌套列表中删除子列表(查找子列表的索引)
- mongodb - 数组中嵌入文档的部分索引
- android - 在 React Native 应用程序中实现指纹登录
- c - C语言:Segmentation Fault: 11. Unclear error
- karate - 是否可以在空手道中进行软断言
- azure - 从 APIM 通过 IP 联系 IIS
- python - 将 pandas 过滤器应用于数据框会给出一个充满 NaN 的数据框
- r - 使用对数转换色标时的 ggplotly 鼠标值