内联函数在不同的转换单元中具有不同的编译器标志未定义的行为?

phö*_*hön 7 c++ one-definition-rule inline-functions language-lawyer translation-unit

在visual studio中,您可以为各个cpp文件设置不同的编译器选项.例如:在"代码生成"下,我们可以在调试模式下启用基本运行时检查.或者我们可以改变浮点模型(精确/严格/快速).这些只是一些例子.有很多不同的旗帜.

只要定义相同,就可以在程序中多次定义内联函数.我们将此函数放入标题中并将其包含在多个翻译单元中.现在,如果不同的cpp文件中的不同编译器选项导致该函数的编译代码略有不同,会发生什么?然后他们确实不同,我们有不确定的行为?你可以使函数静态(或将它放入一个未命名的命名空间)但是更进一步,直接在类中定义的每个成员函数都是隐式内联的.这意味着如果这些cpp文件共享相同的编译器标志,我们可能只包含不同cpp文件中的类.我无法想象这是真的,因为这基本上是容易出错.

我们在未定义行为的土地上真的那么快吗?还是编译器会处理这种情况?

sup*_*cat 3

就标准而言,命令行标志的每种组合都会将编译器转变为不同的实现。虽然实现能够使用其他实现生成的目标文件很有用,但标准没有强制要求它们这样做。

即使没有内联,也可以考虑在一个编译单元中使用以下函数:

char foo(void) { return 255; }
Run Code Online (Sandbox Code Playgroud)

以及另一个中的以下内容:

char foo(void);
int arr[128];
void bar(void)
{
  int x=foo();
  if (x >= 0 && x < 128)
     arr[x]=1;
}
Run Code Online (Sandbox Code Playgroud)

如果char是两个编译单元中的有符号类型,则第二个单元中的值x将小于零(从而跳过数组分配)。如果它在两个单元中都是无符号类型,则它将大于 127(同样跳过赋值)。但是,如果一个编译单元使用有符号char而另一个编译单元使用无符号,并且如果实现期望在结果寄存器中返回值进行符号扩展或零扩展,则结果可能是编译器可能会确定不能x更大小于 127,即使它包含 255,或者它不能小于 0,即使它包含 -1。因此,生成的代码可能会访问arr[255]or arr[-1],从而带来潜在的灾难性结果。

虽然在许多情况下使用不同编译器标志组合代码应该是安全的,但该标准没有努力区分这种混合是安全的和不安全的。

  • @phön:编译的某些方面需要在翻译单元之间保持一致;其他部分则不然。实施的文档通常应明确哪些部分是哪些部分,但这是标准管辖范围之外的实施质量问题。我的观点并不是说改变标志通常是危险的,但它并不“总是”安全。 (2认同)
  • @curiousguy:就标准而言,C 或 C++ 实现将一堆源文件作为输入,以及要运行的程序的任何输入,并自动执行生成输出所需的所有操作,无论是什么程序应该产生。标准没有“持久”目标文件的概念,并且没有定义任何可以在程序的任何部分开始编译阶段 1 的时间和程序的时间之间更改任何源文件或编译器的配置的方法。执行完毕。 (2认同)