为什么我的变量在Debug模式下被初始化,而在Release模式下却没有被初始化?

3

我有一个使用VC++ dot net 3构建的Windows表单应用程序。

有一个整数变量x。

当我以调试模式运行应用程序时,x的值为0;但是当我在没有调试的情况下运行应用程序时,x的值会变得非常大,达到113901996。

该变量已初始化为int x = 0。

为什么会这样?我需要检查哪些设置吗?


3
请简单地发布一些代码,一切可能会变得清晰明了。 - Kieren Johnstone
可以更新一下吗?你尝试将变量输出到 cout 并得到相同的结果了吗?我在回答中付出了一些努力,希望能得到一些反馈。 - Luchian Grigore
实际上,如果您从方法返回变量,则它确实存在。尽管有所争论,但我的答案是正确的——您没有正确初始化。这与优化无关。这可以总结为“返回的变量未初始化”。这意味着这是一个初始化问题,而不是优化问题。该变量确实存在并已分配。 - Kieren Johnstone
除非您发布一些汇编代码,否则您无法确定这一点。 您不是编译器。 您只是根据直觉假设它执行任何操作。 “如果您从方法返回变量,则该变量确实存在”-真的吗? 您能提供标准提取以支持此说法吗? 变量可以像我在答案中证明的那样被优化掉(请注意,由二进制代码证明)。 仅因为您在代码中写下它并不意味着它必须存在于二进制文件中。 - Luchian Grigore
你可以在你的回答中证明1+1=2,但就像你的优化想法一样,这并不相关。关键是:如果你从一个函数返回一个变量,那么它必须存在。事实上,它有一个看似随机的大值,这证明了它的存在 - 如果这个变量不存在且未初始化,那么这个值从哪里来?显然它确实存在,而且未初始化。作为一个局部变量,它将获得堆栈上该部分的任何值。根据你的理论,113901996是从哪里来的? - Kieren Johnstone
显示剩余3条评论
2个回答

3

编辑:根据您提供的新信息,您忘记初始化变量,这种行为是可以预料的。您正在读取垃圾内存。

现在,即使您初始化了变量,只要不以影响程序的可观察行为的方式使用它,仍然可以复制此行为。这就是我最初的答案所处理的内容 - 我会将其留在这里供以后参考。

您实际上是否在使用 x?如果您只是初始化它,优化器很可能会排除整个过程。

如果它没有影响程序的可观察行为,优化器可以自由地从二进制文件中剪切任何内容。

监视器也可能欺骗您,在发布模式下我见过这种情况。

您能发布一些最小化的代码来复制问题吗?

如果您通过一些输出进行测试,例如:

cout << x;

我确定你会发现x0
编辑:
为了澄清事情:
int main()
{
   int x = 0;
   x = 3;
   return 0;
}

在发布模式下(启用完整优化),将翻译为:

00401000  xor         eax,eax 
00401002  ret  

这个变量不仅没有被赋值,它甚至不存在;与调试版本相反:

00411370  push        ebp  
00411371  mov         ebp,esp 
00411373  sub         esp,0CCh 
00411379  push        ebx  
0041137A  push        esi  
0041137B  push        edi  
0041137C  lea         edi,[ebp-0CCh] 
00411382  mov         ecx,33h 
00411387  mov         eax,0CCCCCCCCh 
0041138C  rep stos    dword ptr es:[edi] 
0041138E  mov         dword ptr [x],0 
00411395  mov         dword ptr [x],3 
0041139C  xor         eax,eax 

这段代码中创建了一个变量x并且赋值两次(参见 mov dword ptr [x],0mov dword ptr [x],3 )。

如果变量x修改了可观察的输出,即:

int main()
{
   int x = 0;
   x = 3;
   cout << x;
   return 0;
}

生成的二进制文件将如下所示:
00401000  mov         ecx,dword ptr [__imp_std::cout (40203Ch)] 
00401006  push        3    
00401008  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> 
0040100E  xor         eax,eax 

可以看出,即使如此,x 实际上并没有生成,而是将值 3 放在堆栈上直接打印。

但在这种情况下观察到的行为是相同的,因为打印的是值 3。对于用户来说,变量 x 的值应为 3 是否被打印或直接打印 3 都是无关紧要的。

关键是:如果程序的行为没有被某些语句改变,则可以将它们排除在外。在您的情况下,甚至都没有创建变量 x


1
我对这个答案有点困惑,难道不是变量在发布模式下没有初始化默认值,只有在调试模式下才有吗?其他优化讨论几乎无关紧要。 - Kieren Johnstone
1
@Cody - 这个答案是错误的,根据 OP 的说法,现在他初始化了变量,它按预期工作。他指的是并返回变量,这意味着它不能被优化掉,它将始终存在。Luchian,也许我可以得到一个道歉,因为你一再攻击我? - Kieren Johnstone
1
Luchian,你是对的 - “如果它不影响程序的观察行为”。很明显,一个返回113901996的函数并不是预期的结果,因此这样的优化会导致这种情况。这显然是由未初始化的内存引起的,正如OP本人所说!当他们初始化它时,它开始工作了 - 这怎么能更清楚呢? - Kieren Johnstone
1
最后,你的回答以“你实际上在使用x吗?”开头。结果,他们确实在使用它。它是一个在函数中返回的局部变量。优化并不意味着变量可能会得到值113901996,但初始化是必须的。 - Kieren Johnstone
1
如果通过初始化变量来解决问题(确实是这样),那么问题就在于它未被初始化并且取了一个看似随机的值。没有比这更明显的了。根据OP所说,我投了反对票,因为完全是误导性的。这里绝对没有任何与优化有关的事情发生。 - Kieren Johnstone
显示剩余16条评论

0

在发布模式下,变量不会被初始化为它们的默认值,即0(如果您没有指定初始值)。

这是一种强制用户初始化所有变量的方法。在C++中,你真的应该这样做。其他语言如C#会为你默认设置为0/其他默认值,但在C++中,由你决定。

我认为你的问题在于初始化。你说你正在分配一个初始值为0,但当你测量时,你得到了11301996。要么你没有按照你的期望进行初始化(我的最佳猜测),要么你的输出/测试程序存在问题。发帖你的代码,我很乐意提供进一步的帮助。

(编辑:确实,通过初始化你的变量,问题得到了解决。这并不意味着变量被“优化掉”了,显然不是这样——优化器永远不会优化代码以返回大的、随机的值,而期望的是零。)


1
我没有在参考标准,我说的是Visual Studio编译器。快速谷歌一下可以发现很多人说同样的话,“在发布模式下,局部变量不会被初始化为0”。我可能错过了xaria手动设置为0的部分,但同样的,如果不使用变量和调试,你认为他们如何读取该值呢? - Kieren Johnstone
1
那么,如果他们没有使用变量,又没有调试器的情况下,你认为他们是如何读取X的值的? - Kieren Johnstone
1
我是说,你认为他们如何在不使用调试器的情况下看到价值“113901996”(症状)?这正是OP所问的情况-当他们不使用调试器时,他们看到了那个值。这与变量未被使用/引用如何相符? - Kieren Johnstone
1
你已经和自己继续讨论了一段时间。这里是原帖作者说他们没有使用调试器的地方:“但是当我在不使用调试器的情况下运行应用程序时,值非常大113901996。”。再见。 - Kieren Johnstone
1
谢谢Luchian,你的回答帮了很多忙。 - xaria
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接