C语言readLine()函数偶发SIGTRAP错误的原因及修复咨询
你的readLine函数触发SIGTRAP的原因及修复方案
问题分析
你的代码触发SIGTRAP(断点陷阱)本质是堆内存越界破坏,根源在两个地方:
扩容逻辑完全错误
你用sizeOfLine记录可容纳的字符数(不含\0),初始值是1(因为2-1)。当输入字符数达到1时,你调用realloc申请2*1=2字节的空间,但紧接着把sizeOfLine重新设为2*1-1=1——等于扩容后缓冲区的可用字符数还是1!
后续输入更多字符时,会不断触发扩容,但缓冲区实际大小始终只有2字节,写入line[numOfChars-1]必然越界,直接破坏堆的内存结构,触发SIGTRAP。未处理输入异常
如果scanf("%c", &c)读取失败(比如遇到EOF),c的值不会更新,循环会一直判断c != '\n',导致numOfChars无限增长,最终要么内存耗尽,要么堆越界崩溃。
修复后的代码
修正扩容逻辑,同时处理输入错误:
#include <stdio.h> #include <stdlib.h> char *readLine() { // 初始总容量:2字节(1个字符 + 1个'\0') size_t capacity = 2; char *line = malloc(capacity); if (line == NULL) { fprintf(stderr, "MEM_GRESKA\n"); exit(1); } size_t numOfChars = 0; int c; // 用int存getchar返回值,能识别EOF(值为-1) // 用getchar代替scanf,更高效且便于处理EOF while ((c = getchar()) != '\n' && c != EOF) { // 检查是否需要扩容:当前字符数+1(新字符)+1('\0')超过容量 if (numOfChars + 2 > capacity) { capacity *= 2; char *temp = realloc(line, capacity); if (temp == NULL) { free(line); // 扩容失败先释放旧内存,避免泄漏 fprintf(stderr, "MEM_GRESKA\n"); exit(1); } line = temp; } line[numOfChars++] = (char)c; } // 终止字符串 line[numOfChars] = '\0'; // 缩容到实际需要的大小(可选,不缩容也不影响正确性) char *finalLine = realloc(line, numOfChars + 1); if (finalLine != NULL) { line = finalLine; } return line; }
关键改进点
- 用
capacity直接管理缓冲区总字节数(含\0),扩容逻辑清晰:每次容量翻倍,彻底避免越界。 - 替换
scanf("%c")为getchar(),用int接收返回值,能正确识别EOF,防止死循环。 - 扩容时用临时变量接收
realloc结果,避免原指针丢失(如果realloc失败,旧内存还能正常释放)。 - 去掉不必要的
malloc/realloc强制类型转换,C语言中void*可自动转为char*,强制转换反而可能隐藏类型错误。
其他可行的行读取实现方式
- POSIX标准的
getline函数
Linux/macOS环境下可直接用该函数,自动管理内存,无需手动扩容:
#include <stdio.h> #include <stdlib.h> char *readLine() { char *line = NULL; size_t capacity = 0; ssize_t len = getline(&line, &capacity, stdin); if (len == -1) { free(line); // 即使读取失败,也要释放可能分配的内存 fprintf(stderr, "MEM_GRESKA\n"); exit(1); } // 可选:去掉末尾的换行符 if (len > 0 && line[len-1] == '\n') { line[len-1] = '\0'; } return line; }
注意:getline是POSIX标准,非C标准,Windows环境需用_getline或其他替代实现。
- 固定缓冲区+按需扩容
如果想先使用固定大小缓冲区,再按需扩容,可参考:
#define INIT_BUFFER_SIZE 1024 char *readLine() { char *line = malloc(INIT_BUFFER_SIZE); if (line == NULL) { fprintf(stderr, "MEM_GRESKA\n"); exit(1); } size_t pos = 0; size_t capacity = INIT_BUFFER_SIZE; int c; while ((c = getchar()) != '\n' && c != EOF) { if (pos >= capacity - 1) { // 缓冲区满,扩容为原来的2倍 size_t newCapacity = capacity * 2; char *temp = realloc(line, newCapacity); if (temp == NULL) { free(line); fprintf(stderr, "MEM_GRESKA\n"); exit(1); } line = temp; capacity = newCapacity; } line[pos++] = (char)c; } line[pos] = '\0'; return line; }
内容的提问来源于stack exchange,提问作者Vukašin Jovanović
相关产品推荐
相关产品推荐

