UB何时发生,它是否会使所有先前参与的合同无效?

时间:2019-06-10 18:39:49

标签: c++ language-lawyer undefined-behavior memory-model

C或C ++语言语义与用户形成矛盾。一些构造对它们的行为没有任何限制,这是由于在某些情况下(例如,取消引用未指向对象的指针,例如空指针)没有指定的行为,或者是被构造为明确未定义。无论哪种情况,都不能保证以下行为。

那过去呢?这些已定义行为并产生输出的指令。我想可以删除输出,但是以前的交互可能在过去已经观察到。

未定义的行为是否可以预见,这样就不会出现某些输出?例如:

std::cout << "hello, world" << std::endl; // with a flush
float f = 1./0.; // UB: cancels previous syscall?

这里是否符合write的系统调用(假设Unix)?

现在该内存模型如何?可以保证对原子对象,互斥锁以及所有顺序一致的操作的所有操作均具有顺序(每个命令均与指令流一致,但不一定要有并集);如果程序表现出未定义的行为,什么时候适用保证?

实现可以在程序执行的某个时候使用未定义的行为作为不遵守内存模型要求的借口吗?换句话说,具有语言语义的合同客户(用户)可以在什么时候期望这些要求(在I / O上,在操作顺序上)得到交付?

说明:只有格式正确的程序

(我意识到我可能没有我想要的那么具体。)

某些程序的源代码违反了一致性或健全性规则:

  • 违反一个定义规则
  • 没有有效专业化的模板
  • 在不同点绑定到不同名称的模板

被描述为完全无效。编译器可以通过诊断拒绝这些程序,也可以对其进行编译,但是在那种情况下,该程序的执行未定义行为。我们称其为“ 先验 UB”。

问题不在于那些程序,而是关于至少在一段时间内可以执行良好定义的格式良好的程序。

2 个答案:

答案 0 :(得分:13)

  

未定义的行为是否可以预见,这样就不会出现某些输出?例如:...不会在这里进行write系统调用(假设Unix)吗?

是的,这符合要求。参见[intro.execution] / 5:

  

执行格式正确的程序的符合性实现应产生与可观察到的行为相同的行为   具有相同程序的抽象机的相应实例的可能执行之一   和相同的输入。但是,如果任何此类执行包含未定义的操作,则本文档将   不需要使用该输入来执行该程序的实现(甚至对于   第一个未定义操作之前的操作)。

由于您的程序包含无条件 UB,因此该标准对程序的任何运行对实施的行为没有任何要求。

请注意,如果UB取决于输入,例如:

int main() 
    double x;
    std::cout << "Please enter a number: ";
    std::cin >> x;
    std::cout << "The reciprocal of your number is: " << 1/x << std::endl;
};

[intro.execution] /7.3中的规定适用:

  

交互式设备的输入和输出动态必须以这样的方式进行:在程序等待输入之前,实际上会发出提示输出。交互式设备的构成是实现定义的。

因此,该程序不允许等待输入非零值,然后才输出消息Please enter a number:。它必须先打印出消息。因此,从这个意义上讲,UB的范围仅限于在输入了足够多的输入以确定行为未定义的点之后发生的事件。

答案 1 :(得分:2)

该标准允许实施人员提供超出其要求的行为保证。给出类似的东西:

unsigned char ch = getc();
printf("Oh no %d\n", 1/ch + ((-ch)<<1));

该标准不承认执行可能达到getc()而又没有达到printf的任何情况,因此,在这种情况发生时,它对程序行为提出了任何要求。可能发生这种情况的实现(例如,由于control-C触发了SIGINT),并且希望程序员能够利用所产生的语义,这些实现可以并且应该提供超出标准要求的适当保证,但是存在以下问题:是否以及何时提供此类担保不在标准的管辖范围之内。