首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在bison中调用yyrestart函数,没有导致El Capitan上sigsev的参数

在bison中调用yyrestart函数,没有导致El Capitan上sigsev的参数
EN

Stack Overflow用户
提问于 2015-10-27 19:17:25
回答 1查看 2.1K关注 0票数 1

我对yyrestart的函数签名很好奇--即在lexer文件中,我看到签名是:

代码语言:javascript
运行
复制
void yyrestart  (FILE * input_file )

在我的代码中,我使用yyrestart刷新缓冲区,但我没有传递任何参数,它只是空的:

代码语言:javascript
运行
复制
yyrestart();

它目前正在我们测试的每个系统上运行,除了最新版本的OS。通过GDB,在我的流变机上可以清楚地看到,在没有参数调用的情况下,文件指针设置为NULL:

代码语言:javascript
运行
复制
yyrestart (input_file=0x0) at reglexer.c:1489

而在El Capitan上,它作为垃圾出现,这将导致生成的代码后面出现mem错误:

代码语言:javascript
运行
复制
yyrestart (input_file=0x100001d0d) at reglexer.c:1489

在我的一生中,我无法确定yyrestart()是在哪里定义的。在yacc/flex中是否有一些宏定义了不带参数调用yyrestart的行为?如果没有,这是如何编译的呢?

*编辑澄清编译问题*

作为一个小片段来了解我在说什么--这就是我的.y文件中的内容,它正在执行解析器(这是对这个例子的一个微小的修改):

代码语言:javascript
运行
复制
int main() {

FILE *myfile = fopen("infile.txt", "r");

if (!myfile) {
    fprintf(stderr, "can't open infile.txt\n");
    return 1;
}

calcYYin = myfile;

do {
    calcYYparse();
} while (!feof(calcYYin));

calcYYrestart();
return 0;
}

我可以用我想要的任何东西来构建存储库,并将其作为参数传入到该行上的calcYYrestart()。代用

代码语言:javascript
运行
复制
calcYYrestart('a', 1, 5, 'a string');

仍然允许我使用make编译整个程序(但是get是一个输入错误的片段)。但是在查看生成的parcalc.c文件时,除了文件指针之外,没有任何东西允许我调用calcYYrestart。我只把它看作是原型:

代码语言:javascript
运行
复制
void calcYYrestart  (FILE * input_file );

编译器的神奇之处在于,它允许我将任何我想要的东西作为参数放到生成的函数中?

EN

Stack Overflow用户

回答已采纳

发布于 2015-10-28 00:17:08

你期待着C能轻轻地带领你穿过迷宫,握着你的手,当你犯错的时候责备你,并为你的成功鼓掌。

这可能不是对一种语言的不合理的期望,但C不是那种语言。做你让它做的事情,没有更多,当你的指令不够清晰时,它只会让你跌倒。

不过,作为辩护,你可以要求它再详细一点。如果在命令行中指定-Wall (至少使用gcc和clang),编译器将向您提供一些警告。见附注1。

在这种情况下,它可能会警告您没有声明calcYYrestart,这将使您有责任正确地处理参数。这个函数是在lexer中声明和定义的,但是在这里您要在解析器中使用它,解析器是一个单独的编译单元。您确实应该在解析器序言中声明它,但是没有什么可以强制声明的正确性。(在这种情况下,C++将无法链接,但是C不会在形式函数名称中记录参数类型。)

值得注意的是,您的工作所依据的示例代码存在许多问题。我建议寻找一个更好的野牛/ flex教程,或者至少阅读flex手册中关于如何处理输入的章节。

在这里,我向原始示例添加了一些注释,其中显示了calc.y野牛输入文件:

代码语言:javascript
运行
复制
/* This is unnecessary, since `calcYYparse` is defined in this file.
extern int calcYYparse();
*/

extern FILE *calcYYin;

/* Command line arguments are always good */
int main(int argc, char** argv) {
    /* If there is an argument, use it. Otherwise, stick with stdin */
    /* There is no need for a local variable. We can just use yyin */
    if (argc > 1) {
        calcYYin = fopen(argv[1], "r");
        if (!calcYYin) {
            fprintf(stderr, "can't open infile.txt\n");
            return 1;
        }
}
/* calcYYin = myfile; */

/* This loop is unnecessary, since yyparse parses input until it
 * reaches EOF, unless it hits an error. And if it hits an error, it
 * will call calcYYerror (below), which in turn calls exit(1), so it
 * never returns.
 */
/* do { */
    calcYYparse();
/* } while (!feof(calcYYin)); */
    return 0;
}

void calcYYerror(const char* s) {
    fprintf(stderr, "Error! %s\n", s);
    /* Valid arguments to `exit` are 0 and small positive integers. */ 
    exit(EXIT_FAILURE);
}

当然,如果你碰到语法错误,你可能不想把这个世界搞砸。这样做的目的可能是放弃行的其余部分,然后继续解析。在这种情况下,出于明显的原因,callYYerror不应该调用exit()

默认情况下,在调用yyerror之后,yyparse会立即返回(在清理其本地存储之后),并带有错误指示。如果您想继续使用它,那么您需要使用error产品,这将是最好的解决方案。

您也可以简单地再次调用yyparse,如本例所示。但是,这在flex缓冲区中留下了未知数量的输入文件。没有理由相信缓冲区错误地包含了行的其余部分。由于flex扫描器通常以大块形式读取输入(交互输入除外),用yyrestart重置输入文件将丢弃随机数量的输入,将输入文件指针留在文件中的任意位置,这可能与新行的开头不对应。

即使不是这种情况,就像非缓冲(交互式)输入一样,完全有可能在行尾检测到错误,在这种情况下,新行已经被消耗掉了。因此,丢弃到当前行的末尾将导致在错误之后丢弃行。

最后,使用feof(input)终止输入循环是众所周知的反模式,应该避免在读取输入时遇到EOF时终止。对于flex生成的扫描器,当检测到EOF时,将丢弃当前的输入,然后(如果yywrap不能成功地创建新的输入),END指示将返回到解析器。到那时,yyin不再有效(因为它被丢弃了),对它调用feof是未定义的行为。

备注

  1. 还可以通过指定-Wextra来获得更多的警告。你可以让编译器更严格一点,让它使用最新的标准,-std=c11,而不是1989年版本的各种gcc扩展,现在大多已经过时了。)
票数 2
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33376700

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档