C语言表达式求值程序循环触发段错误,请求问题排查
数学表达式求值程序段错误排查与修复
问题背景
将C++版本的数学表达式求值程序转译为C语言后,处理表达式时触发段错误。传入测试字符串为"20*2+1",循环执行第二次时出错。目录结构如下:
├── main.c ├── math │ ├── include.h │ └── main.c ├── stack │ ├── include.h │ └── main.c
核心错误分析
1. 栈结构体的柔性数组未分配内存
Stack结构体中的data[]是柔性数组成员,仅声明数组大小而未分配实际内存。原stack_init函数仅初始化了top和max_height,但没有为data分配内存,导致后续stack_push/stack_pop访问data时直接越界,触发段错误。
2. stack_top函数逻辑错误
原stack_top返回的是栈顶索引st->top,但代码中却用它来判断栈顶的操作符(比如stack_top(&ops) != '('),完全不符合逻辑——应该返回栈顶的实际元素值,而非索引。
3. 未初始化变量导致的未定义行为
evaluate函数中,char c;未初始化就执行printf("c %c\n",c);,会读取随机内存值,引发未定义行为。
4. 数字解析循环的索引错误
在解析多位数的while循环中,c = token[i];的位置错误,导致读取的字符滞后一位,且索引操作可能越界。
修复后的代码实现
栈模块(stack/include.h)
#ifndef STACK_H #define STACK_H #include <stdio.h> typedef struct{ int top, max_height; float data[]; }Stack; // 修改为返回指针,动态分配内存 Stack* stack_init(int max); void stack_push(Stack* st, float value); void stack_replace_top(Stack* st, float value); void stack_print(Stack* st); float stack_pop(Stack* st); int stack_full(Stack* st); // 返回栈顶元素值,而非索引 float stack_top(Stack* st); int stack_height(Stack* st); int stack_empty(Stack* st); // 添加销毁函数,避免内存泄漏 void stack_destroy(Stack* st); #endif
栈模块(stack/main.c)
#include <stdio.h> #include <stdlib.h> #include "include.h" Stack* stack_init(int max){ // 分配结构体+柔性数组的总内存 Stack* st = malloc(sizeof(Stack) + sizeof(float) * max); if(st == NULL){ perror("Failed to allocate stack"); exit(EXIT_FAILURE); } st->top = 0; st->max_height = max; return st; } void stack_destroy(Stack* st){ free(st); } float stack_top(Stack* st){ if(stack_empty(st)){ fprintf(stderr, "Stack underflow on top\n"); exit(EXIT_FAILURE); } return st->data[st->top - 1]; } int stack_height(Stack* st){return st->max_height;} void stack_push(Stack* st, float value){ if(stack_full(st)){ fprintf(stderr, "Stack overflow on push\n"); exit(EXIT_FAILURE); } st->data[st->top] = value; (st->top)++; } float stack_pop(Stack* st){ if(stack_empty(st)){ fprintf(stderr, "Stack underflow on pop\n"); exit(EXIT_FAILURE); } (st->top)--; return st->data[st->top]; } int stack_full(Stack* st){return (st->top >= st->max_height);} void stack_replace_top(Stack* st, float value){ if(stack_empty(st)){ fprintf(stderr, "Stack underflow on replace top\n"); exit(EXIT_FAILURE); } st->data[st->top - 1] = value; } int stack_empty(Stack* st){return (st->top <= 0);} void stack_print(Stack* st){ int i; if(st->top == 0){ printf("Stack Is Empty.\n"); }else{ printf("Stack Contents:\n"); for(i=0;i<st->top;i++){ printf("%g\t",st->data[i]); } printf("\n\n"); } }
数学求值模块(math/main.c)
#include <math.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include "include.h" #include "../stack/include.h" static float precedence(char op){ switch(op){ case '+':case '-':return 1; case '*':case '/':return 2; case '^':case '%':return 3; default: return 0; } return 0; } static float apply_op(float a,float b,char op){ switch(op){ case '+': return a + b; case '-': return a - b; case '*': return a * b; case '/': if(b == 0){ fprintf(stderr, "Division by zero\n"); exit(EXIT_FAILURE); } return a / b; case '^': return pow(a, b); case '%': return fmod(a, b); } return 0; } float evaluate(char* text){ // 若输入无空格,strtok直接返回原字符串 char* token = strtok(text, " "); if(token == NULL){ fprintf(stderr, "Empty expression\n"); exit(EXIT_FAILURE); } Stack* values = stack_init(100); Stack* ops = stack_init(100); int i=0; int size = strlen(token); printf("Eval: %s\n",token); printf("4: %c\n",token[4]); for(;i<size;i++){ printf("size %d\n",size); printf("i %d\n",i); char c = token[i]; // 先初始化c再使用 printf("c %c\n",c); printf("passed\n"); switch(c){ case '0':case '1':case '2':case '3':case '4': case '5':case '6':case '7':case '8':case '9':{ float val = 0; // 循环读取多位数 while(i < size && token[i] >= '0' && token[i] <= '9'){ val = (val*10) + (token[i] - '0'); i++; } stack_push(values, val); i--; // 抵消for循环的i++ break; } case '(' :{ stack_push(ops, (float)'('); break; } case ')':{ while(!stack_empty(ops) && (char)stack_top(ops) != '('){ float val2 = stack_pop(values); float val1 = stack_pop(values); char op = (char)stack_pop(ops); stack_push(values, apply_op(val1, val2, op)); } stack_pop(ops); // 弹出'(' break; } case '+': case '-': case '*': case '/': case '^': case '%':{ while(!stack_empty(ops) && precedence((char)stack_top(ops)) >= precedence(c)){ float val2 = stack_pop(values); float val1 = stack_pop(values); char op = (char)stack_pop(ops); stack_push(values, apply_op(val1, val2, op)); } stack_push(ops, (float)c); break; } // 忽略空格(如果有的话) case ' ': break; default: fprintf(stderr, "Invalid character: %c\n", c); exit(EXIT_FAILURE); } } // 处理剩余操作符 while(!stack_empty(ops)){ float val2 = stack_pop(values); float val1 = stack_pop(values); char op = (char)stack_pop(ops); stack_push(values, apply_op(val1, val2, op)); } float result = stack_pop(values); // 释放内存 stack_destroy(values); stack_destroy(ops); return result; }
修复说明
- 栈内存分配:改用动态分配内存的方式创建栈,确保柔性数组
data有足够空间存储元素。 - stack_top修正:返回栈顶的实际元素值,并在栈空时添加错误处理。
- 变量初始化:提前初始化
c再进行打印和判断,避免未定义行为。 - 数字解析逻辑:修正while循环中读取字符的方式,确保正确读取每一位数字,避免索引越界。
- 内存泄漏修复:添加
stack_destroy函数,使用完栈后释放内存。
内容的提问来源于stack exchange,提问作者Cheese Danish
相关产品推荐
相关产品推荐

