gpt4 book ai didi

在没有参数的情况下在 bison 中调用 yyrestart 函数导致 El Capitan 上出现 sigsegv

转载 作者:太空宇宙 更新时间:2023-11-04 02:39:40 24 4
gpt4 key购买 nike

我很好奇 yyrestart 的函数签名 - 即在词法分析器文件中我看到签名是:

void yyrestart  (FILE * input_file )

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

yyrestart();

除了最新版本的 OS X 之外,它目前在我们测试的每个系统上都在运行。通过 GDB 单步执行,很明显在我的 rhel 机器上,不带参数的调用将文件指针设置为 NULL:

yyrestart (input_file=0x0) at reglexer.c:1489

而在 El Capitan 上它作为垃圾出现,这会导致稍后在生成的代码中出现内存错误:

yyrestart (input_file=0x100001d0d) at reglexer.c:1489

我这辈子都弄不清楚 yyrestart() 的定义位置。 yacc/flex 中是否有一些宏定义了不带参数调用 yyrestart 的行为?如果不是,这甚至是如何编译的?

************ 编辑以澄清编译问题 ************

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

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() 来构建该存储库。替换

calcYYrestart('a', 1, 5, 'a string');

仍然让我使用 make 编译整个程序(但是得到一个带有错误输入的 segv)。但是查看生成的 parcalc.c 文件,除了文件指针之外,我看不到任何可以让我调用 calcYYrestart 的东西。我只将其视为原型(prototype):

void calcYYrestart  (FILE * input_file );

编译器让我可以将任何我想要的内容作为生成函数的参数的神奇之处在哪里?

最佳答案

你期待 C 温柔地带领你穿过迷宫,牵着你的手,在你犯错时责备你,为你的成功鼓掌。

这些对一门语言的期望或许并不是不合理的,但C不是那门语言。 C 按照您的指示执行,仅此而已,当您的指令不够清晰时,它只会让您跌倒。

虽然,在它的辩护中,你可以要求它更冗长一点。如果您在命令行中指定 -Wall(至少使用 gcc 和 clang),编译器将为您提供一些警告。 [见注释 1。]

在这种情况下,它可能会警告您未声明 calcYYrestart,这将使您有责任正确设置参数。该函数是在词法分析器中声明和定义的,但在这里您是在解析器中使用它,这是一个单独的编译单元。你真的应该在解析器序言中声明它,但没有任何东西会强制声明的正确性。 (在这种情况下,C++ 将无法链接,但 C 不会在正式函数名称中记录参数类型。)


值得注意的是,您作为工作基础的示例代码存在很多问题。我建议寻找更好的 bison/flex 教程,或者至少阅读 flex 手册中有关如何处理输入的部分。

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

/* 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 而不是增加了各种 gcc 扩展的 1989 版本(现在大部分已经过时)来使编译器更严格一些。)

关于在没有参数的情况下在 bison 中调用 yyrestart 函数导致 El Capitan 上出现 sigsegv,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33376700/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com