下面的代码片段在发布版本中发生了什么?
What's happening with the snippet below in a release build?
下面的代码生成了一个悬空引用,这可以从编译器发出的警告以及函数g()
中A
对象的析构函数在函数返回之前被调用的事实中看出。还可以验证在"使用堆栈"之后的main()
中,返回的引用是否具有垃圾,至少在调试构建中是这样。但我无法在发布版本中重现相同的行为。为什么?编译器在这里进行了什么样的优化,以给人一种引用r
可以的印象?
#include <iostream>
struct A{
A(int i) : i(i) { std::cout << "Ctorn"; }
A(const A& a) { i = a.i; std::cout << "Copy ctorn"; }
~A() { std::cout << "Dtorn"; }
int i;
};
A& g(int i) { A x(i); return x; }
int main()
{
const A& r = g(1);
std::cout << "Using the stackn";
std::cout << r.i << 'n'; // r.i has garbage in debug, but not in a release build.
}
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";
这只是未定义的行为。你通过引用返回一个临时的,任何事情都可能发生。
A& g(int i) { A x(i); return x; }
是非法的。
调试生成可能会清除内存并导致错误,因为内存已被清除。
发布版本并不麻烦。你用什么付费,对吧?它只是保持内存不变,但将其标记为可由操作系统回收。之后所有的手套都脱了。
这是VC++编译器带来的一件(可以说)好事。您将在调试构建中看到各种各样的事情来帮助您。。。好调试得更好。未初始化的指针设置为某个特定值,以便知道它未初始化,在delete
之后内存归零,以便知道它已被删除。这有助于更快地发现问题,因为在发布版本中,如果内存没有被覆盖,你可能仍然会看到内存,或者访问未初始化的指针并使其出现以工作,等等。否则你看不到的问题,以及你当时发现的问题,会造成很大的伤害,而且很难诊断。
以下是Visual Studio 2012 64位的速度优化(/O2编译器切换)版本构建在运行此代码并打印出一个代码时的实际操作:
int main()
{
000000013F7C7E50 sub rsp,28h
const A& r = g(1);
000000013F7C7E54 lea rdx,[string "Ctorn" (013F83DA4Ch)]
000000013F7C7E5B lea rcx,[std::cout (013F85FAA0h)]
000000013F7C7E62 call std::operator<<<std::char_traits<char> > (013F7C1500h)
000000013F7C7E67 lea rdx,[string "Dtorn" (013F83DA54h)]
000000013F7C7E6E lea rcx,[std::cout (013F85FAA0h)]
000000013F7C7E75 call std::operator<<<std::char_traits<char> > (013F7C1500h)
std::cout << "Using the stackn";
000000013F7C7E7A lea rdx,[string "Using the stackn" (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)
请注意,它甚至不需要真正创建和销毁A
对象。它只需要调用cout
四次。每次,rdx
都保存要打印的对象。前三个打印字符串"Ctor"、"Dtor"answers"使用堆栈"。最后一个看起来只是打印edx
中的整数,即1
。
编译器确实可以对未定义的行为执行任何操作。它除了打印一个用于空间优化的(/O1编译器开关)之外,还打印一些OP发现的未优化的(/Od)。
- 此测试()中发生了什么意外过程?为什么总是覆盖 ch[0 1 2..]?
- 这C++代码中发生了什么C++(指数函数)
- 哪种方式更快?究竟发生了什么,我们没有看到什么?
- 我正在将一个 std::string 传递给一个 boost 函数,该函数对该类型进行常量引用,但该值发生了变化
- c++问题:给一个变量赋值后,另一个变量发生了变化
- 从"LLONG_MAX 秒"构造 std::chrono::毫秒变量时发生了什么?
- 这个片段中关于 n 在 pc[i] 中的表示发生了什么
- istringstream,num1 和 num2 在这里发生了什么?
- 究竟发生了什么,我们需要在 c++ 中双重调度/访客
- libstdc++的make_shared布局在gcc 4.x和gcc 6.x之间是否发生了变化?
- 我是否能够确定在部署一个程序后发生了什么,我在数组末尾写入?
- 为什么我的 int 在 C++ 程序中间发生了变化?
- 这个阶乘程序内部发生了什么?
- C++ - *(int**) 地址?这里发生了什么?
- C++用用户输入在循环中填充 char 数组:输入整个字符串时到底发生了什么?
- 在使用VS2010构建ACE和TAO(CORBA)时发生了许多错误
- 为什么在波纹管程序中发生了从字符串常量到'char*'的警告已弃用的转换
- 让对象知道它创建的 show 函数中发生了什么
- 下面的代码片段在发布版本中发生了什么?
- c++索引变量在调试版本中发生了根本性的变化