使用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; }
三、编译与运行步骤
- 生成Lex代码:
flex lexer.l - 生成YACC代码:
yacc -d parser.y - 编译可执行文件:
gcc y.tab.c lex.yy.c -o parser - 运行测试:
./parser,输入a=b+c后回车,会输出对应的中间代码。
内容的提问来源于stack exchange,提问作者Anonymoustez
相关产品推荐
相关产品推荐

