【发布时间】:2015-07-03 12:54:30
【问题描述】:
根据对“未定义行为”的现代解释,编译器有权假设不会发生导致未定义行为“不可避免”的事件链,并且可以消除仅适用于以下情况的代码将执行未定义的行为;这可能会导致未定义行为的影响及时向后工作,并使原本可以观察到的行为无效。另一方面,如果除非程序终止,否则未定义行为是不可避免的,但如果程序可以并且确实在调用未定义行为之前终止,则程序的行为将保持完全定义。
在做出此决定时,编译器需要考虑哪些终止原因?举几个例子:
在许多平台上,对“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;
}
}
如果程序被调用,argc 等于 3,但 SIGINT 阻止了 getc() 调用完全返回,是否会定义行为?当然,如果有任何 getc() 可以返回的 值 会导致定义的行为,那么在编译器可以确定不会接收到这样的输入之前,不会发生未定义的行为。如果没有值 getc() 可以返回,这将避免未定义的行为,但是,如果 getc() 被阻止返回任何值,整个程序是否会保持定义? getc() 的返回值和调用未定义行为的动作之间是否存在因果关系会影响事物(在上面的示例中,编译器无法知道会发生任何特定形式的未定义行为不知道输入了什么字符,但任何可能的输入都会触发某种形式)。
同样,如果在一个平台上存在地址,如果读取这些地址会导致程序立即终止,编译器会指定易失性读取将触发硬件读取请求,并且该平台上的某些外部库指定它将返回指向这样一个地址的指针,这些因素是否暗示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 编译器通过什么方式确定给定操作是否可能导致程序执行以它不知道的方式终止,以及在什么情况下允许在此类操作之前重新安排未定义行为?
【问题讨论】:
-
听起来你想让我们解决Halting Problem
-
我不明白问题的重点。如果某些事情超出了编译器的控制范围,你希望它怎么做?
-
这个问题最终归结为“C 编译器能否确定给定的操作是否会终止程序?” 这是停止问题。但接下来的问题是,“以它不知道的方式?” 答案是,“呃,不。”
-
@user3386109:问题基本上是编译器是否可以使用一个动作不会终止程序的假设作为声明程序执行未定义的基础,而实际上该动作将终止在调用未定义行为之前对程序进行排序。
-
@WeatherVane:我不是在试图“让编译器为我完成工作”——相反,我试图了解确保禁止编译器的必要条件按照 C 标准从做我不希望它做的事情。
标签: c language-lawyer undefined-behavior