String 循环内的变量是否在每次迭代时未重新初始化?

String 循环内的变量是否在每次迭代时未重新初始化?,string,c++-cli,scope,String,C++ Cli,Scope,我有一个C++/CLI项目,它在for循环中声明一个String^变量,但不初始化它。在第一次迭代中,变量被设置为某个值。在随后的每一天 迭代时,它似乎保留了以前的值。局部作用域中的变量不应该每次通过循环初始化为null(或等效值)吗?这种情况也发生在int上。还有,编译器 除非我将警告级别设置为W4,否则不会警告可能未初始化的值,即使如此,它也只警告int而不是字符串^ 这是显示行为的示例代码 #include "stdafx.h" using namespace System; int m

我有一个C++/CLI项目,它在for循环中声明一个String^变量,但不初始化它。在第一次迭代中,变量被设置为某个值。在随后的每一天 迭代时,它似乎保留了以前的值。局部作用域中的变量不应该每次通过循环初始化为null(或等效值)吗?这种情况也发生在int上。还有,编译器 除非我将警告级别设置为W4,否则不会警告可能未初始化的值,即使如此,它也只警告int而不是字符串^

这是显示行为的示例代码

#include "stdafx.h"
using namespace System;

int main(array<System::String ^> ^args)
{
    for(int n = 0; n < 10; n++)
    {
        String^ variable;
        int x;

        switch(n)
        {
        case 1:
            variable = "One";
            x = 1;
            break;
        case 5:
            variable = "Five";
            x = 5;
            break;
        }

        Console::WriteLine("{0}{1}", variable, x);
    }
}
我是否完全误解了局部作用域变量应该如何初始化?这是管理C++特有的“特性”吗?如果我皈依
对于C语言编译器将警告这两个变量,即使在基本警告级别。

< P>免责声明:我知道C和C++相当好;C++/CLI,没有那么多。但是你看到的行为和C或C++中的类似程序基本相同。

<代码>字符串^ < /Cord>是一个“代码>字符串的句柄,类似于C或C++中的指针。

除非C++/CLI为句柄的初始化添加新规则,否则没有显式初始化的
String^
类型的块作用域变量最初将有一个垃圾值,由内存块中发生的任何内容组成

循环的每次迭代在概念上创建和销毁在
{
}
之间定义的任何变量。每个迭代可能会在相同的内存位置分配其局部变量(这不是必需的,但没有真正的理由不这样做)。编译器甚至可以生成代码,将条目上的内存分配给函数

因此,在循环的第一次迭代中,
变量
设置为
“One”
(或者更确切地说,设置为引用
“One”
)的句柄,这是
控制台::WriteLine打印的值。没问题

在第二次迭代中,
变量
被分配到第一次迭代中使用的相同内存位置。没有给它分配新值,因此它保留了第一次迭代时存储在该内存位置的值。同样的事情也发生在
x

您不能指望保留上一个值,并且程序的行为未定义。如果您的目标是编写一个正确工作的程序,而不是理解这个错误程序的行为,那么解决方案就是确保所有变量在使用之前都已正确初始化

如果您在第二次迭代而不是第一次迭代中完成了初始赋值,那么程序可能会在第一次迭代中崩溃——尽管这还不能保证

至于为什么编译器没有对此发出警告,我不知道。我不太愿意提出一个编译器错误,但这可能是一个

此外,即使启用了高警告级别,关于未初始化变量的警告也需要进行默认情况下可能无法完成的控制流分析。同时启用警告和高级优化可能会为编译器提供足够的信息来警告
变量
x


它警告
x
而不是
variable
W4

C++/CLI只是标准C++的一个扩展/超集,因此它符合其大多数规范,仅扩展它以适应CLI(~.Net)要求,这似乎仍然很奇怪

局部作用域中的变量不应该初始化为null(或 每次通过回路时的等效值

AFIK C++标准没有定义局部循环变量应该初始化的方式。< /P> 因此,为了避免任何开销,编译器通常不对循环使用特定的本地内存管理:请参见以下问题:

我是否完全误解了局部作用域变量应该如何初始化

这是托管C独有的“功能”吗++

<0 >,这不是一个特性或特殊行为:您的C++/CLI编译器只使用标准C++实践。 如果我将其转换为C#,编译器将对这两个变量发出警告, 即使在基本警告级别

C#和AFAIK Java努力避免任何未定义的行为,因此它们迫使您在使用局部变量之前初始化它们

以下是编译产生的CIL(我已经做了一些格式化和注释,以使这组文本可以理解:):

因此,编译器生成的代码实际上从来不会隐式地操纵变量

One, 1
One, 1
One, 1
One, 1
Five, 5
Five, 5
Five, 5
Five, 5
Five, 5
.locals init (int32 V_0, int32 V_1, string V_2, int32 V_3)
//                   ^          ^           ^          ^
//                   n          x        variable     tmp

// initialization of "n"
IL_0000:  ldc.i4.0
IL_0001:  stloc.0
IL_0002:  br.s       IL_0008

// loop starts here

// post iteration processing
IL_0004:  ldloc.0
IL_0005:  ldc.i4.1
IL_0006:  add
IL_0007:  stloc.0

// stop condition check
IL_0008:  ldloc.0
IL_0009:  ldc.i4.s   10
IL_000b:  bge.s      IL_003e

// initialization of temporary "tmp" variable for switch
IL_000d:  ldloc.0
IL_000e:  stloc.3

// check if "tmp" is 3
IL_000f:  ldloc.3
IL_0010:  ldc.i4.1
// if so go to "variable" intialization
IL_0011:  beq.s      IL_0019

// check if "tmp" is 5
IL_0013:  ldloc.3
IL_0014:  ldc.i4.5
IL_0015:  beq.s      IL_0023

// go to display
IL_0017:  br.s       IL_002b

// initialization of "variable"
IL_0019:  ldstr      "One"
IL_001e:  stloc.2
...