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

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;
}

修复说明

  1. 栈内存分配:改用动态分配内存的方式创建栈,确保柔性数组data有足够空间存储元素。
  2. stack_top修正:返回栈顶的实际元素值,并在栈空时添加错误处理。
  3. 变量初始化:提前初始化c再进行打印和判断,避免未定义行为。
  4. 数字解析逻辑:修正while循环中读取字符的方式,确保正确读取每一位数字,避免索引越界。
  5. 内存泄漏修复:添加stack_destroy函数,使用完栈后释放内存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 01:03:05