gpt4 book ai didi

如果 getc() 通过 SIGINT 退出,则 getc() 之后的未定义行为是否可以改变程序行为

转载 作者:太空狗 更新时间:2023-10-29 15:05:40 27 4
gpt4 key购买 nike

根据“未定义行为”的现代解释,编译器有权假设不会发生导致未定义行为“不可避免”的事件链,并且可以消除仅适用于以下情况的代码:将执行未定义的行为;这可能会导致未定义行为的影响及时倒退,并使原本可以观察到的行为无效。另一方面,如果除非程序终止,否则未定义行为将不可避免,但程序可以并且确实在调用未定义行为之前终止,程序的行为将保持完全定义。

在做出这个决定时,编译器需要考虑哪些终止原因?举几个例子:

在许多平台上,对像“getc”这样的函数的调用通常会返回(至少最终),但在某些超出编译器控制的情况下不会返回。如果有这样的程序:

int main(int argc, char *argv[])
{
if (argc != 3)
{
printf("Foo\n");
return 0;
}
else
{
int ch;
printf("You'd better type control-C!\n");
int ch = getc();
if (ch < 65)
return (ch-33) / (argc-3);
else
return INT_MAX + ch;
}
}

如果使用等于 3 的 argc 调用程序,但 SIGINT 阻止 getc() 调用返回,是否会定义行为?当然,如果 getc() 可以返回任何,这将导致定义的行为,则在编译器可以确定此类输入不会发生之前,不会发生未定义的行为已收到。如果没有值 getc() 可以返回,这将避免未定义的行为,但是,如果 getc() 被阻止返回任何值,整个程序是否会保持定义值(value)? getc() 的返回值与调用未定义行为的操作之间是否存在因果关系会影响事情吗(在上面的示例中,编译器无法知道任何特定 在不知道输入什么字符的情况下会发生未定义行为的形式,但任何可能的输入都会触发某种形式)。

同样,如果在平台上存在地址,如果读取这些地址,则指定会导致程序立即终止,编译器指定 volatile 读取将触发硬件读取请求,并且该平台上的某些外部库指定它会返回指向这样一个地址的指针,这些因素是否意味着 bar 在这个单独的例子中的行为:

int foo(int x)
{
char volatile * p = get_instant_quit_address();
if (x)
{ printf("Hey"); fflush(stdout); }
return *p / x; // Will cause UB if *p yields a value and x is zero
}
int bar(void)
{
return foo(0);
}
如果尝试读取 *p 实际上会立即终止程序执行而不产生值,那么

将被定义(作为终止而没有打印任何东西)?在返回一个值之前,除法不能继续;因此,如果没有返回值,则不会除以零。

允许 C 编译器通过什么方式确定给定的操作是否可能导致程序执行以它不知道的方式终止,以及在什么情况下允许在此类操作之前重新安排未定义的行为?

最佳答案

这在 [intro.execution] 下的 C++ 中有很好的描述:

5 - A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible executions of the corresponding instance of the abstract machine with the same program and the same input. However, if any such execution contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).

普遍认为 C 具有相同的特性,因此 C 编译器可以类似地执行 "time travel"存在未定义的行为。

重要的是,请注意问题是是否存在表现出未定义行为的抽象机实例;您可以通过先终止程序执行来安排防止机器上出现未定义的行为,这并不重要。

如果您使程序以抽象机器无法摆脱的完全定义的方式自行终止,您可以防止未定义的行为(以及由此产生的时间旅行)。例如,在您的第二个示例中,如果您将对 *p 的访问替换为 (exit(0), 0) 那么未定义的行为就不会发生,因为不可能执行exit 返回给它的调用者的抽象机。但是无论您的平台有何特点,抽象机都不必在访问 insta-kill 地址时终止您的程序(事实上,抽象机没有任何 insta-kill 地址)。

关于如果 getc() 通过 SIGINT 退出,则 getc() 之后的未定义行为是否可以改变程序行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29855620/

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