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

C/C++中访问char**指针触发Segmentation Fault的问题排查求助

解决你的表达式分词函数段错误问题

嘿,作为C/C++初学者,碰到指针和内存管理的坑太正常了!你遇到的Segmentation Fault核心原因其实很明确:你分配的char** arr是一个指针数组,但数组里的每个char*都是未初始化的野指针——它们根本没指向任何可写的内存空间,直接操作这些指针必然会触发非法内存访问。

咱们一步步拆解你的代码问题,再给出修正方案:

1. 指针数组的致命错误

你写的这行:

char **arr = (char**)malloc(1024 * sizeof(char*));

确实分配了能存1024个char*的空间,但每个arr[index]都是随机的垃圾地址,没有指向有效的内存块。这里你犯了一个典型的错误:

  • 你注释掉的arr[index] = intTocharStar(num);其实是正确写法!因为intTocharStar已经用malloc给字符串分配了内存,直接把这个内存地址赋值给arr[index]就完成了存储;
  • 而你改成的strcpy(arr[index], intTocharStar(num));完全错误——arr[index]没有指向可写的内存,strcpy会往随机地址写数据,直接触发段错误。

2. 低级参数传错问题

在else分支处理操作符的时候,你写了:

strcpy(arr[index], convertChar(num));

这里明显传错参数了!应该是convertChar(op)而不是convertChar(num)——你要转换的是当前解析到的操作符op,不是之前的整数num,这个错误会导致转换出错误的字符,甚至引发其他异常。

3. stringstream循环的逻辑漏洞

你的循环条件while (parser)会导致循环多执行一次。当parser解析到输入末尾时,它的状态会变成fail,但循环还是会再跑一次,这时候parser >> num或parser >> op都会失败,变量值会异常,甚至触发错误。正确的做法是结合peek()和解析操作来控制循环,还要跳过空白字符(不然空格会被当成操作符处理)。

4. 内存管理的冗余与隐患

  • 你最后重新malloc了res,把arr的元素拷贝过去再freearr——这步没问题,但要注意:arr里的每个元素都是malloc出来的,赋值给res后,这些内存的所有权转移到了res,后续调用者需要逐个free每个token,再freeres本身,否则会内存泄漏;
  • 你给res分配了固定的100个char*空间,如果分词后的token数量超过100,会导致数组越界,最好根据实际的index值分配刚好合适的空间。

修正后的完整代码

#include <iostream>
#include <sstream>
#include <cctype>
#include <cstdlib>
#include <cstring>

using namespace std;

char* intTocharStar(int x) { 
    int length = snprintf( NULL, 0, "%d", x ); 
    char* str = (char*)malloc( length + 1 ); 
    snprintf( str, length + 1, "%d", x ); 
    return str; 
} 

char* convertChar(char c) { 
    char* pointer = (char*)malloc(2 * sizeof(char)); 
    pointer[0] = c; 
    pointer[1] = '\0'; 
    return pointer; 
} 

char **tokenize(const char* input) { 
    // 先分配足够大的临时指针数组
    char **arr = (char**)malloc(1024 * sizeof(char*)); 
    int index = 0; 
    stringstream parser(input); 

    while (!parser.eof()) {
        char peek = parser.peek();
        // 跳过所有空白字符
        if (isspace(peek)) {
            parser.ignore();
            continue;
        }
        // 判断当前是数字还是操作符
        if (isdigit(peek)) { 
            int num;
            parser >> num; 
            cout << "Num " << num << '\n'; 
            arr[index++] = intTocharStar(num); 
        } else { 
            char op;
            parser >> op; 
            cout << "Op " << op << '\n'; 
            arr[index++] = convertChar(op); 
        } 
    } 

    // 分配刚好合适大小的结果数组,最后加NULL标记结束
    char** res = (char **)malloc((index + 1) * sizeof(char*)); 
    for (int i = 0; i < index; i++) {
        res[i] = arr[i]; 
    } 
    res[index] = NULL; // 方便调用者遍历到末尾
    free(arr); // 只释放临时指针数组本身,元素内存已转移到res
    return res; 
}

// 测试用例
int main() {
    const char* input = "(10 - 2) / 2";
    char** tokens = tokenize(input);
    // 遍历输出所有token
    for (int i = 0; tokens[i] != NULL; i++) {
        cout << "Token " << i << ": " << tokens[i] << endl;
        free(tokens[i]); // 逐个释放每个token的内存
    }
    free(tokens); // 最后释放指针数组本身
    return 0;
}

实用的调试技巧

  1. 用GDB精准定位:编译时加-g参数(比如g++ -g yourcode.cpp -o yourcode),然后启动gdb ./yourcode,输入run运行程序,触发段错误后输入bt查看调用栈,能直接看到出错的代码行和原因;
  2. 打印指针地址:在关键位置加cout << "arr[" << index << "] addr: " << (void*)arr[index] << endl;,你会发现它是随机的垃圾地址,这就能坐实野指针的问题;
  3. 启用编译器警告:编译时加-Wall -Wextra,编译器会帮你揪出很多潜在问题,比如未初始化的变量、参数类型不匹配等。

内容的提问来源于stack exchange,提问作者Dunkler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 23:57:46