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

使用YACC构建算术表达式解析器时遇Segmentation Fault求助

解决YACC算术表达式解析器的段错误与类型兼容问题

我帮你梳理下代码里的几个关键问题,这些问题直接导致了类型兼容警告和段错误:

一、核心问题分析

1. YACC语法规则完全缺失

你的YACC文件中%% %%之间没有任何语法匹配规则,yyparse()执行时没有可遵循的解析逻辑,这是触发段错误的核心原因之一。

2. 类型不匹配导致内存越界

  • 你在%union中定义了存储代码、地址的结构体,但把ID和NUM的%type设为单个char类型,而Lex识别出的ID(比如"a")、NUM(比如"123")都是字符串,类型不匹配会直接导致内存访问越界,触发段错误。
  • Lex文件没有把匹配到的ID/NUM内容传递给YACC的yyval,YACC无法正确获取变量名或数值,进一步加剧了类型混乱。

3. 数组初始化溢出

char var[2]="t0";和char label[2]="L1";存在缓冲区溢出:字符串"t0"包含两个字符加一个结束符\0,数组大小至少需要3,否则会破坏内存中的其他数据。

4. Lex规则语法错误

Lex的动作代码必须包裹在{}内,比如{digit}+后面的return NUM;如果不放在{}里,Lex无法正确生成词法分析代码,导致token识别异常。


二、修正后的完整代码

1. 修正后的YACC文件(parser.y)

%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int yylex();
void yyerror(const char *s);

// 用计数器替代固定数组,避免缓冲区溢出
int temp_count = 0;
int label_count = 0;
%}

%union {
    struct attributes {
        char code[200];  // 放大缓冲区,避免代码过长溢出
        char addr[50];
        char op[3];
    } type_id;
    char str[50];  // 专门存储ID和NUM的字符串值
}

%start L
%token <str> ID NUM
%token WHILE OR AND NOT TRUE FALSE DO END GE LE EE NE UMINUS

%right '='
%left AND OR
%left '<' '>' LE GE NE EE
%left '+' '-'
%left '*' '/'
%right UMINUS
%left '!'

%type <type_id> L E

%%

L : E {
        printf("\nGenerated Intermediate Code:\n%s", $1.code);
    }
  ;

E : ID '=' E {
        strcpy($$.addr, $1);
        sprintf($$.code, "%s = %s\n%s", $1, $3.addr, $3.code);
    }
  | E '+' E {
        temp_count++;
        char temp[50];
        sprintf(temp, "t%d", temp_count);
        strcpy($$.addr, temp);
        sprintf($$.code, "%s\n%s = %s + %s", $1.code, temp, $1.addr, $3.addr);
    }
  | E '-' E {
        temp_count++;
        char temp[50];
        sprintf(temp, "t%d", temp_count);
        strcpy($$.addr, temp);
        sprintf($$.code, "%s\n%s = %s - %s", $1.code, temp, $1.addr, $3.addr);
    }
  | E '*' E {
        temp_count++;
        char temp[50];
        sprintf(temp, "t%d", temp_count);
        strcpy($$.addr, temp);
        sprintf($$.code, "%s\n%s = %s * %s", $1.code, temp, $1.addr, $3.addr);
    }
  | E '/' E {
        temp_count++;
        char temp[50];
        sprintf(temp, "t%d", temp_count);
        strcpy($$.addr, temp);
        sprintf($$.code, "%s\n%s = %s / %s", $1.code, temp, $1.addr, $3.addr);
    }
  | ID {
        strcpy($$.addr, $1);
        strcpy($$.code, "");
    }
  | NUM {
        strcpy($$.addr, $1);
        strcpy($$.code, "");
    }
  | '(' E ')' {
        strcpy($$.addr, $2.addr);
        strcpy($$.code, $2.code);
    }
  ;

%%

#include "lex.yy.c"

void yyerror(const char *s) {
    printf("Error: %s\n", s);
}

int main() {
    printf("Enter the expression: ");
    yyparse();
    return 0;
}

2. 修正后的Lex文件(lexer.l)

%{
#include "y.tab.h"
%}

alpha [A-Za-z]
digit [0-9]

%%

[ \t\n]       { /* 忽略空白字符 */ }
{digit}+      { strcpy(yyval.str, yytext); return NUM; }
{alpha}({alpha}|{digit})* { strcpy(yyval.str, yytext); return ID; }
"while"       { return WHILE; }
"do"          { return DO; }
"end"         { return END; }
"true"        { return TRUE; }
"false"       { return FALSE; }
"&&"          { return AND; }
"||"          { return OR; }
"!"           { return NOT; }
">="          { return GE; }
"<="          { return LE; }
"=="          { return EE; }
"!="          { return NE; }
.             { return yytext[0]; }

%%

int yywrap() {
    return 1;
}

三、编译与运行步骤

  1. 生成Lex代码:flex lexer.l
  2. 生成YACC代码:yacc -d parser.y
  3. 编译可执行文件:gcc y.tab.c lex.yy.c -o parser
  4. 运行测试:./parser,输入a=b+c后回车,会输出对应的中间代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 12:22:32