下面的代码片段在发布版本中发生了什么?

Wal*_*ldB 5 c++

下面的代码生成一个悬空引用,可以在编译器发出的警告中看到,并且在函数返回之前调用A函数中对象的析构g()函数.还可以验证在main()"使用堆栈"之后,返回的引用具有垃圾,至少在调试版本中是这样.但我无法在发布版本中重现相同的行为.这是为什么?编译器在这里做了什么样的优化,给人的印象是引用r是OK?

#include <iostream>

struct A{
    A(int i) : i(i) { std::cout << "Ctor\n"; }
    A(const A& a) { i = a.i; std::cout << "Copy ctor\n"; }
    ~A() { std::cout << "Dtor\n"; }
    int i;
};

A& g(int i) { A x(i); return x; }

int main()
{
    const A& r = g(1);
    std::cout << "Using the stack\n";     
    std::cout << r.i << '\n';   // r.i has garbage in debug, but not in a release build.
}
Run Code Online (Sandbox Code Playgroud)

PS.我会反对NRVO,因为该函数不会返回一个A对象.

编辑:回应Mark Tolonen.即使我const A& r = g(1);在发布版本中没有显示垃圾后包含这些表达式std::cout << r.i << '\n';

std::cout << "Using the stack ...................................................................................................................\n";
std::cout << "Using the stack ...................................................................................................................\n";
std::cout << "Using the stack ...................................................................................................................\n";
std::cout << "Using the stack ...................................................................................................................\n";
Run Code Online (Sandbox Code Playgroud)

Luc*_*ore 11

这只是未定义的行为.您通过引用返回临时,任何事情都可能发生.

A& g(int i) { A x(i); return x; }是非法的.

调试版本可能会清除内存并导致错误,因为内存已被清除.

发布版本不会打扰.你支付你使用的费用,对吗?它只是保持记忆不变,但是它被操作系统标记为可回收.之后所有手套都关闭了.

这是VC++编译器带来的(可以说是)好事.你会看到在调试版本中发生的所有事情都可以帮助你......好吧......调试得更好.未初始化的指针设置为某个特定值,以便您知道它未初始化,内存在一个之后清零,delete以便您知道它已被删除.这有助于更快地发现问题,因为在发布版本中,您可能仍然会看到内存,如果它没有被覆盖,或者访问未初始化的指针并使其看起来有效等等.否则您将看不到的问题,以及在你发现的时候会造成很大的伤害并且很难诊断出来.

  • @Elazar UB说*允许任何事情*发生.我解释了为什么(至少尝试过),但对UB的推理几乎没有任何意义.避免它. (2认同)

Mar*_*nen 1

以下是 Visual Studio 2012 64 位的速度优化(/O2 编译器开关)发行版本在运行此代码并打印出代码时实际执行的操作:

int main()
{
000000013F7C7E50  sub         rsp,28h  
    const A& r = g(1);
000000013F7C7E54  lea         rdx,[string "Ctor\n" (013F83DA4Ch)]  
000000013F7C7E5B  lea         rcx,[std::cout (013F85FAA0h)]  
000000013F7C7E62  call        std::operator<<<std::char_traits<char> > (013F7C1500h)  
000000013F7C7E67  lea         rdx,[string "Dtor\n" (013F83DA54h)]  
000000013F7C7E6E  lea         rcx,[std::cout (013F85FAA0h)]  
000000013F7C7E75  call        std::operator<<<std::char_traits<char> > (013F7C1500h)  
    std::cout << "Using the stack\n";     
000000013F7C7E7A  lea         rdx,[string "Using the stack\n" (013F83DA60h)]  
000000013F7C7E81  lea         rcx,[std::cout (013F85FAA0h)]  
000000013F7C7E88  call        std::operator<<<std::char_traits<char> > (013F7C1500h)  
    std::cout << r.i << '\n';   // r.i has garbage in debug, but not in a release build.
000000013F7C7E8D  lea         rcx,[std::cout (013F85FAA0h)]  
000000013F7C7E94  mov         edx,1  
000000013F7C7E99  call        std::basic_ostream<char,std::char_traits<char> >::operator<< (013F7C1384h)  
000000013F7C7E9E  mov         dl,0Ah  
000000013F7C7EA0  mov         rcx,rax  
000000013F7C7EA3  call        std::operator<<<std::char_traits<char> > (013F7C10EBh)  
Run Code Online (Sandbox Code Playgroud)

请注意,它甚至不需要真正创建和销毁A对象。它所做的只是调用cout四次。每次,rdx持有要打印的对象。前三个打印字符串“Ctor\n”、“Dtor\n”和“Using the stack\n”。最后一个看起来只是打印其中edx是 a 的整数1

编译器确实可以对未定义的行为做任何事情。除了空间优化(/O1 编译器开关)之外,它还打印一些内容,或者如 OP 发现的那样,未优化(/Od)。