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

