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

Valgrind报Invalid read of size 1错误求排查(字符串处理场景)

中缀转后缀的Valgrind越界读取问题

我正在编写一个小型计算器程序,它接收包含数学表达式的字符串作为输入,并返回double类型的结果。处理过程中需要将中缀输入字符串转换为后缀表达式,转换函数代码执行正常且能得到正确结果,但Valgrind报出如下错误:

==169915== Invalid read of size 1
==169915==    at 0x109BC5: InfixToPostfix (calculator.c:281)
==169915==    by 0x109540: Calculator (calculator.c:151)
==169915==    by 0x4840EC9: TestCalculate (calculator_test.c:59)
==169915==    by 0x4840FFA: main (calculator_test.c:71)
==169915==  Address 0x5242d7f is 1 bytes before a block of size 64 alloc'd
==169915==    at 0x4E050C5: malloc (vg_replace_malloc.c:393)
==169915==    by 0x1094BF: Calculator (calculator.c:133)
==169915==    by 0x4840EC9: TestCalculate (calculator_test.c:59)
==169915==    by 0x4840FFA: main (calculator_test.c:71)
==169915== 
Test Calc status:         PASSED
Test Calc result:         PASSED

==169915== 
==169915== HEAP SUMMARY:
==169915==     in use at exit: 0 bytes in 0 blocks
==169915==   total heap usage: 24 allocs, 24 frees, 18,352 bytes allocated
==169915== 
==169915== All heap blocks were freed -- no leaks are possible
==169915== 
==169915== For lists of detected and suppressed errors, rerun with: -s
==169915== ERROR SUMMARY: 8 errors from 6 contexts (suppressed: 0 from 0)

上述错误信息重复出现多次,以下是两个核心函数:分配postfix内存的Calculator函数,以及转换函数InfixToPostfix:

calc_status_t Calculator(calc_t *calc, const char *expression, double *result)
{
    stack_t *stack = StackCreate(calc->capacity, sizeof(double));
/*133*/char *postfix = malloc(calc->capacity * sizeof(char));
    char *temp = postfix;
    state_t state = START;
    event_t event = 0;
    input_t input = 0;
    calc_status_t status_handler = 0;

    if (NULL == stack || NULL == postfix)
    {
        return ALLOCATION_ERROR;
    }

    status_handler = ErrorHandler(stack, (char*)expression, calc->capacity);
    if (SUCCESS != status_handler)
    {
        return status_handler;
    }
    
    status_handler = InfixToPostfix(expression, postfix);
    if (SUCCESS != status_handler)
    {
        return status_handler;
    }

    while ('\0' != *postfix && SUCCESS == status_handler)
    {
        input = *postfix;
        event = calc->event[state][input];
        state = calc->event_handler[state][event](stack, &postfix, &status_handler);            
    }

    
    *result = StackIsEmpty(stack) ? 0 : *(double*)StackPeek(stack);

    StackDestroy(stack);
    free(temp);
    return status_handler;
}



static calc_status_t InfixToPostfix(const char *infix, char *postfix)   /* function to convert from infix to postfix, it adds spaces between*/
{                                               
/* and two adjacent chars and handles all precedence cases*/
    int i = 0, j = 0;
    int len = strlen(infix);
    char x = 0;
    int do_need_space = 0;
    char stack[MAX_EXPR_SIZE];
    int top = -1;

    assert(NULL != infix);
    assert(NULL != stack);

    for (i = 0, j = 0; i < len; ++i)
    {
        if (isspace(infix[i]))
        {
            if (isdigit(infix[i - 1]) && isdigit(infix[i + 1]))
            {
                return SYNTAX_ERROR;
            }
            do_need_space = 1;
        }
        if (isalnum(infix[i]))
        {
            postfix[j++] = infix[i];
            x = infix[i + 1];
            if (0 != isdigit(x))
            {
                do_need_space = 1;
            }

        }
        else if ('(' == infix[i])
        {
            stack[++top] = infix[i];
            do_need_space = 1;
        }
        else if (')' == infix[i])
        {
            while(top > -1 && stack[top] != '(')
            {   
                postfix[j++] = stack[top--];
                do_need_space = 0;
            }
            top--;
        }
        else if (IsOperator(infix[i]))
        {
            /* handle operators input as per precedence value*/
            while (top > -1 && (GetPriority(stack[top]) >= GetPriority(infix[i])))
            {
                postfix[j++] = stack[top--];
            }
            stack[++top] = infix[i];
            do_need_space = 1;
        }
        /* handle cases where ++ or --, add space between the two signs*/
/*281*/ if ((postfix[j - 1] == '+' || postfix [j - 1] == '-') && postfix[j - 1] == postfix[j - 2])
        {
            postfix[j - 1] = ' ';
            postfix[j] = postfix[j - 2];
        }
        if(0 == do_need_space)
        {
            postfix[j++] = ' ';
        }           
        do_need_space = 0;
    }
    /* empty the stack after finish reading the infix string*/
    while (top > -1)
    {
        if(stack[top] != '(')
        {   
            postfix[j++] = stack[top--];
            postfix[j++] = ' ';
        }
    }
    if (isspace(postfix[j - 1]))
    {
        postfix[j - 1] = '\0';
    }
    else
    {
        postfix[j] = '\0';
    }
    return SUCCESS;
}

问题分析与修复

1. 核心越界原因

Valgrind报错指向InfixToPostfix函数的281行,该行直接访问postfix[j-1]和postfix[j-2],但未判断j的取值范围:

  • 当j=0时,j-1=-1;当j=1时,j-2=-1,此时访问负索引会读取到分配内存块之前的非法区域,触发Invalid read错误。

2. 修复核心代码

修改281行的判断条件,先确保j至少为2,保证索引合法:

if (j >= 2 && (postfix[j - 1] == '+' || postfix[j - 1] == '-') && postfix[j - 1] == postfix[j - 2])

3. 其他潜在越界修复

处理空格的代码中,直接访问infix[i-1]和infix[i+1],当i=0(表达式开头是空格)或i=len-1(表达式结尾是空格)时会越界,需添加边界判断:

if (isspace(infix[i]))
{
    // 仅当i不是首尾字符时,才判断前后是否为数字
    if (i > 0 && i < len - 1 && isdigit(infix[i - 1]) && isdigit(infix[i + 1]))
    {
        return SYNTAX_ERROR;
    }
    do_need_space = 1;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 15:04:56