为何无法通过#undef重定义Lex的input和unput函数?编译报错解析
问题:重定义Lex的input/unput函数失败,编译报错
我在运行《Lex & Yacc》书中的示例,目的是重定义Lex的input和unput函数,让解析逻辑从命令行参数而非标准输入读取内容。已经把书中代码几乎原封不动适配为ANSI C,但始终无法成功重定义这两个函数,编译时出现冲突错误。
相关代码
%{ #undef input #undef unput int input(void); void unput(int ch); /* LEXICON RELATED VARIABLES */ unsigned verbose; char *prog_name; /* PARSING RELATED VARIABLES */ char **targv; char **arglim; static unsigned offset = 0 ; %} %% -h | "-?" | -help { printf("usage is: %s [ -help | -h | -? ] [ -verbose | -v ] [ (-file | -f) filename ]\n", prog_name); } -v | -verbose { printf("verbose mode is on\n"); verbose=1; } . ; %% int input(void) { char c; if(targv >= arglim) return(0); /* EOF */ if((c = targv[0][offset++]) != '\0') return(c); targv++; offset = 0; return(' '); } void unput(int ch) { /* AT&T lex sometimes puts back the EOF! */ if(ch == '\0') return; /* ignore, can't put back EOF */ if(offset) { /* back up in current arg */ offset--; return; } targv--; /* back to previous arg */ offset = strlen(*targv); } int main(int argc, char **argv) { prog_name = *argv; targv = argv+1; arglim = argv+argc; yylex(); return 0; }
编译错误信息
$ lex test.l; cc lex.yy.c -ll -o test lex.yy.c:545:12: error: static declaration of 'input' follows non-static declaration static int input ( void ); ^ test.l:5:5: note: previous declaration is here int input(void); ^ lex.yy.c:1154:16: error: static declaration of 'input' follows non-static declaration static int input (void) ^ test.l:5:5: note: previous declaration is here int input(void); ^ 2 errors generated.
问题原因与解决方案
问题根源
现在常用的Lex实现(比如GNU Flex)会自动在生成的lex.yy.c文件中声明static int input(void),而你在.l文件里提前声明了全局作用域的int input(void)——C语言不允许同一个函数先被声明为全局(非static),再被声明为static,因为static限定了函数作用域仅为当前文件,和全局声明的作用域规则冲突,因此触发编译错误。
另外,你使用的#undef input没有效果,因为Flex生成的代码里的input是直接的函数声明,而非宏定义,#undef只能取消宏,对函数声明无效。
解决方案
不需要自己提前声明input和unput,只需告诉Flex不要生成默认的输入函数实现,然后直接在代码中定义自己的版本即可:
- 删除
%{ ... %}块里的#undef input、#undef unput以及int input(void);、void unput(int ch);这几行; - 在
%{ ... %}块中添加#define YY_NO_INPUT,这个宏会让Flex跳过默认的input和unput函数生成; - 保留你自己实现的
input和unput函数在%%之后的代码块中。
修改后的代码示例:
%{ #define YY_NO_INPUT // 禁用Flex默认的input/unput函数生成 /* LEXICON RELATED VARIABLES */ unsigned verbose; char *prog_name; /* PARSING RELATED VARIABLES */ char **targv; char **arglim; static unsigned offset = 0 ; %} %% -h | "-?" | -help { printf("usage is: %s [ -help | -h | -? ] [ -verbose | -v ] [ (-file | -f) filename ]\n", prog_name); } -v | -verbose { printf("verbose mode is on\n"); verbose=1; } . ; %% int input(void) { char c; if(targv >= arglim) return(0); /* EOF */ if((c = targv[0][offset++]) != '\0') return(c); targv++; offset = 0; return(' '); } void unput(int ch) { /* AT&T lex sometimes puts back the EOF! */ if(ch == '\0') return; /* ignore, can't put back EOF */ if(offset) { /* back up in current arg */ offset--; return; } targv--; /* back to previous arg */ offset = strlen(*targv); } int main(int argc, char **argv) { prog_name = *argv; targv = argv+1; arglim = argv+argc; yylex(); return 0; }
编译验证
重新执行编译命令:
lex test.l; cc lex.yy.c -ll -o test
此时编译应该可以正常通过。
补充说明
《Lex & Yacc》针对的是早期的AT&T Lex,而现在主流的GNU Flex有不少行为差异。YY_NO_INPUT是Flex提供的专用宏,用来禁用默认输入函数的生成,这样就能安全地自定义input和unput逻辑了。
内容的提问来源于stack exchange,提问作者M.E.
相关产品推荐
相关产品推荐

