C++ 编译器或定义行为的可能优化

C++ 编译器或定义行为的可能优化,c++,c++11,undefined-behavior,C++,C++11,Undefined Behavior,如果有一个函数不接受任何引用或指针作为参数,它的返回类型是未使用的,并且它没有发出明显离开系统的调用(I/O调用、更改系统时间等),那么它是否保证只修改定义它的类(或者根本不修改) 我能想到的关于这条规则的唯一例外情况如下: void a(int b, int c){ *((int*)b) = c; } int main() { int d=1; a((int)(&d),d+1); return 0; } 这能保证被定义吗?我知道int*和int不必是

如果有一个函数不接受任何引用或指针作为参数,它的返回类型是未使用的,并且它没有发出明显离开系统的调用(I/O调用、更改系统时间等),那么它是否保证只修改定义它的类(或者根本不修改)

我能想到的关于这条规则的唯一例外情况如下:

void a(int b, int c){
    *((int*)b) = c; }

int main() {
    int d=1;
    a((int)(&d),d+1);
    return 0; }
这能保证被定义吗?我知道
int*
int
不必是相同的大小,但是如果它们被定义为相同的大小,那么这是必须的,还是仍然是未定义的行为


我们的目标是看看一个函数是否可以合法地优化(即,如果你能证明它没有副作用,它就可以被删除)。

我认为这属于“定义良好的未定义行为”的范畴;它可能会一直工作(假设sizeof(int*)==sizeof(int)),但它在技术上是未定义的,将来很可能有编译器会完全破坏它。另一个例子是使用并集将浮点的位重新解释为int


另外,如果我没有向您指出LLVM的链接时间优化方向,那我就大错特错了。它的目标是在链接时完全按照您所说的去做。它很棒,在osx上“开箱即用”。他们也有一个很好的简单的例子来说明它的工作原理:

我认为这属于“定义良好的未定义行为”的范畴;它可能会一直工作(假设sizeof(int*)==sizeof(int)),但它在技术上是未定义的,将来很可能有编译器会完全破坏它。另一个例子是使用并集将浮点的位重新解释为int


另外,如果我没有向您指出LLVM的链接时间优化方向,那我就大错特错了。它的目标是在链接时完全按照您所说的去做。它很棒,在osx上“开箱即用”。他们还提供了一个很好的简单示例来说明它的工作原理:

该标准保证用于从指针转换到合适的整数类型(大到足以容纳所有值)并返回到原始指针类型的
重新解释
保证生成相同的指针值。是的,这是有保证的:

int *p = new int(5);
intptr_t i = reinterpret_cast<intptr_t>(p);
// ...
int *q = reinterpret_cast<int*>(i);

assert(p == q);
*q = 10;
assert(*p == 5);
int*p=新的int(5);
intptr_t i=重新解释铸件(p);
// ...
int*q=重新解释(i);
断言(p==q);
*q=10;
断言(*p==5);
编译器可以删除没有副作用的代码,但仅通过检查函数签名无法明确确定。对于内联函数,如果编译器对代码具有可见性,那么编译器就有机会。对于在不同的转换单元中定义的函数来说,这有点困难(通过链接时间优化,如果函数足够小,仍然是可行的)


请注意,这不仅限于通过值或常量引用获取参数的函数。如果编译器看到一个函数通过引用修改参数,但它可以证明修改过的对象的值不会被再次读取,理论上它可以删除该调用。另一方面,除了在简单的情况下,我不会打赌编译器会这样做。

标准保证用于从指针转换到合适的整数类型(大到足以容纳所有值)并返回到原始指针类型的
reinterpret_cast
保证生成相同的指针值。是的,这是有保证的:

int *p = new int(5);
intptr_t i = reinterpret_cast<intptr_t>(p);
// ...
int *q = reinterpret_cast<int*>(i);

assert(p == q);
*q = 10;
assert(*p == 5);
int*p=新的int(5);
intptr_t i=重新解释铸件(p);
// ...
int*q=重新解释(i);
断言(p==q);
*q=10;
断言(*p==5);
编译器可以删除没有副作用的代码,但仅通过检查函数签名无法明确确定。对于内联函数,如果编译器对代码具有可见性,那么编译器就有机会。对于在不同的转换单元中定义的函数来说,这有点困难(通过链接时间优化,如果函数足够小,仍然是可行的)


请注意,这不仅限于通过值或常量引用获取参数的函数。如果编译器看到一个函数通过引用修改参数,但它可以证明修改过的对象的值不会被再次读取,理论上它可以删除该调用。另一方面,除了在简单的情况下,我不会打赌编译器会这样做。

请参阅:and.@artlessnoise这更像是一个例子。而且,这不是死代码,这是活代码,什么也不做。不,我认为(如果我错了,请纠正我)你无法优化它。假设b和c是具有共享指针的类的实例,这可能会受到影响。甚至可以考虑在离开作用域时发生的析构函数调用。@rralf是有意义的。顺便说一句,行为定义了吗?@soandos维基百科的文章没有完全解释这一点,但是有许多编译器优化可以检测到没有任何作用的活代码。那是死代码。请参阅维基百科和。如果编译器研究没有涉及到这一点,那么我认为您的问题近乎深奥。请参阅:@artlessnoise这更多是关于类似的内容。而且,这不是死代码,这是活代码,什么也不做。不,我认为(如果我错了,请纠正我)你无法优化它。假设b和c是具有共享指针的类的实例,这可能会受到影响。甚至可以考虑在离开作用域时发生的析构函数调用。@rralf是有意义的。顺便说一句,行为定义了吗?@soandos维基百科的文章没有完全解释这一点,但是有许多编译器优化可以检测到没有任何作用的活代码。那是死代码。请参阅维基百科和。如果编译器研究没有涉及到这个问题,那么我认为你的问题近乎深奥。