如果getc()通过SIGINT退出,则可以跟随getc()的未定义行为会改变程序行为 [英] Can undefined behavior which would follow a getc() alter program behavior if the getc() exits via SIGINT

查看:138
本文介绍了如果getc()通过SIGINT退出,则可以跟随getc()的未定义行为会改变程序行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Under modern interpretations of "Undefined Behavior", a compiler is entitled to assume that no chain of events which would cause undefined behavior to be "inevitable" will occur, and can eliminate code which would only be applicable in cases where code is going to perform Undefined Behavior; this may cause the effects of Undefined Behavior to work backwards in time and nullify behaviors that would otherwise have been observable. On the other hand, in cases where Undefined Behavior would be inevitable unless a program terminates, but where a program could and does terminate prior to invoking Undefined Behavior, behavior of the program would remain fully defined.

在进行此确定时,需要考虑哪些终止原因?举几个例子:

In making this determination, what causes of termination is a compiler required to consider? As a couple of examples:

在许多平台上,对"getc"之类的函数的调用通常会返回(至少最终会返回),但在某些情况下,编译器无法控制.如果有人有类似这样的程序:

On many platforms, a call to a function like "getc" will normally return (at least eventually), but under some cases outside the control of the compiler will not. If one had a program like:

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()的返回值和调用未定义行为的动作之间是否存在因果关系会影响事物(在上面的示例中,编译器无法知道会出现任何 specific 形式的未定义行为)不知道输入了什么字符,但是任何可能的输入都会触发某种形式.

would behavior be defined in case program was called with argc equal to three, but a SIGINT prevented the getc() call from returning at all? Certainly if there were any value that getc() could return which would result in defined behavior, no Undefined Behavior could occur until the compiler could be certain that such input would not be received. In the event that there is no value getc() could return which would avoid Undefined Behavior, however, would overall program remain defined if getc() was prevented from ever returning any value? Would the existence of a causal relationship between the return value of getc() and the actions invoking Undefined Behavior affect things (in the example above, a compiler could not know that any particular form of Undefined Behavior would occur without knowing what character was input, but any possible input would trigger some form).

同样,如果在平台上存在指定的地址(如果读取的话)导致程序立即终止,则编译器指定易失性读取将触发硬件读取请求,并且该平台上的某个外部库指定它将返回指向该地址的指针,这些因素是否暗示bar在此单独示例中的行为:

Likewise, if on a platform there existed addresses which, if read, were specified to cause a program to immediately terminate, a compiler's specified that volatile reads will trigger hardware read requests, and some external library on that platform specified that it would return a pointer to such an address, would those factors imply that the behavior of bar in this separate example:

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实际上会立即终止程序执行而不会产生任何值,那么将定义

(终止而不打印任何内容)吗?除非返回值,否则除法无法进行;因此,如果不返回任何值,则不会被零除.

would be defined (as terminating without having printed anything) if attempting to read *p would in fact immediate terminate program execution without yielding a value? The division cannot proceed until a value is returned; thus, if no value is returned, there would be no divide by zero.

C编译器被允许以什么方式确定给定的动作是否可能导致程序执行以其不知道的方式终止,并且在什么情况下允许其在此类动作之前重新计划未定义的行为? /p>

By what means is a C compiler allowed to determine whether a given action might cause program execution to terminate in ways that it doesn't know about, and in what cases is it allowed to reschedule Undefined Behavior ahead of such actions?

推荐答案

这在C ++中的 [intro.execution] :

5-执行格式正确的程序的一致性实现应产生与具有相同程序的抽象机的相应实例的可能执行之一相同的可观察行为. 和相同的输入.但是,如果任何这样的执行包含未定义的操作,则本国际标准对使用该输入执行该程序的实现都没有要求(甚至没有 关于第一个未定义操作之前的操作).

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编译器可以类似地执行存在未定义的行为.

It is generally accepted that C has the same characteristics, and so that a C compiler can similarly perform "time travel" in the presence of undefined behavior.

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

Importantly, note that the question is whether there exists an instance of the abstract machine exhibiting undefined behavior; it doesn't matter that you can arrange to prevent undefined behavior on your machine by terminating program execution first.

如果您导致程序以完全定义的方式终止自身,而抽象机无法退出,则您可以 防止未定义的行为(以及由此产生的时间旅行).例如,在第二个示例中,如果将对*p的访问权限替换为(exit(0), 0),则不会发生未定义的行为,因为无法执行exit返回其调用方的抽象机.但是无论平台的特性如何,抽象机都不必在访问Insta-kill地址时终止程序(实际上,抽象机没有任何Insta-kill地址).

You can prevent undefined behavior (and resulting time travel) if you cause the program to terminate itself in a fully-defined way which the abstract machine cannot wriggle out of. For example, in your second example if you replace access to *p with (exit(0), 0) then undefined behavior cannot occur as there is no possible execution of the abstract machine where exit returns to its caller. But whatever the characteristics of your platform, the abstract machine does not have to terminate your program on access to an insta-kill address (indeed, the abstract machine does not have any insta-kill addresses).

这篇关于如果getc()通过SIGINT退出,则可以跟随getc()的未定义行为会改变程序行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆