You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言readLine()函数偶发SIGTRAP错误的原因及修复咨询

你的readLine函数触发SIGTRAP的原因及修复方案

问题分析

你的代码触发SIGTRAP(断点陷阱)本质是堆内存越界破坏,根源在两个地方:

  1. 扩容逻辑完全错误
    你用sizeOfLine记录可容纳的字符数(不含\0),初始值是1(因为2-1)。当输入字符数达到1时,你调用realloc申请2*1=2字节的空间,但紧接着把sizeOfLine重新设为2*1-1=1——等于扩容后缓冲区的可用字符数还是1!
    后续输入更多字符时,会不断触发扩容,但缓冲区实际大小始终只有2字节,写入line[numOfChars-1]必然越界,直接破坏堆的内存结构,触发SIGTRAP。

  2. 未处理输入异常
    如果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*,强制转换反而可能隐藏类型错误。

其他可行的行读取实现方式

  1. 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或其他替代实现。

  1. 固定缓冲区+按需扩容
    如果想先使用固定大小缓冲区,再按需扩容,可参考:
#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ć

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 03:34:57