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
相关产品推荐
相关产品推荐

