3

I have a C++/CLI project that declares a String^ variable inside a for loop but does not initialize it. On the first iteration, the variable is set to some value. On each subsequent iteration, it appears to be retaining the previous value. Shouldn't a variable in local scope be initialized to null (or equivalent) each time thru the loop? This happens with an int as well. Also, the compiler does not warn of a potentially uninitialized value unless I set the warning level to W4, and even then it only warns for the int and not the String^.

This is sample code that shows the behavior.

#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);
    }
}

The output of this will be

One, 1
One, 1
One, 1
One, 1
Five, 5
Five, 5
Five, 5
Five, 5
Five, 5

Am I completely misunderstanding how locally scoped variables are supposed to be initialized? Is this a "feature" unique to managed C++? If I convert this to C# the compiler will warn about both variables, even at the base warning level.

Dan
  • 111
  • 1
  • 6

2 Answers2

1

Disclaimer: I know C and C++ pretty well; C++/CLI, not so much. But the behavior you're seeing is essentially the same that I'd expect for a similar program in C or C++.

String^ is a handle to a String, similar to a pointer in C or C++.

Unless C++/CLI adds new rules for initialization of handles, a block-scope variable of type String^ with no explicit initialization will initially have a garbage value, consisting of whatever happened to be in that chunk of memory.

Each iteration of the loop conceptually creates and destroys any variables defined between the { and }. And each iteration probably allocates its local variables in the same memory location (this isn't required, but there's no real reason for it not to do so). The compiler could even generate code that allocates the memory on entry to the function.

So on the first iteration of your loop, variable is set to "One" (or rather, to a handle that refers to "One"), that's the value printed by Console::WriteLine. No problem there.

On the second iteration, variable is allocated in the same memory location that was used for it on the first iteration. No new value is assigned to it, so it retains the value that was stored in that memory location on the first iteration. The same thing happens with x.

You cannot count on the previous value being retained, and your program's behavior is undefined. If your goal were to write a correctly working program, rather than to understand how this incorrect program behaves, the solution would be to ensure that all your variables are properly initialized before they're used.

If you did the initial assignment on the second iteration rather than the first, the program would likely crash on the first iteration -- though even that's not guaranteed.

As for why the compiler doesn't warn about this, I don't know. I hesitate to suggest a compiler bug, but this could be one.

Also, even with high warning levels enabled, warning about uninitialized variables requires control flow analysis that may not be done by default. Enabling both warnings and a high level of optimization might give the compiler enough information to warn about both variable and x.

It still seems odd that it warns about x and not about variable with W4.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
0

C++/CLI is only an extension/superset of standard C++ so it complies with most of its specifications, extending it only to fit with the CLI (~.Net) requirements.

Shouldn't a variable in local scope be initialized to null (or equivalent) each time thru the loop?

AFAIK the C++ standard does not define the way local loop variables should be initialized.

So, to avoid any overhead, compilers usually don't use specific local memory management for loops : see this SO question : Is there any overhead to declaring a variable within a loop? (C++)

Am I completely misunderstanding how locally scoped variables are supposed to be initialized?

Is this a "feature" unique to managed C++

So no, this is not a feature or special behavior : your C++/CLI compiler is only using standard C++ practices.

If I convert this to C# the compiler will warn about both variables, even at the base warning level.

C# and AFAIK Java try hard to avoid any undefined behaviors, so they force you to initialize local variables before they are used.

Here is the CIL resulting from the compilation (I've done some formatting and commenting to make this bunch of text understandable :)) :

.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
...

So the variable is indeed never implicitly manipulated by the code generated by the compiler.

Community
  • 1
  • 1
Pragmateek
  • 13,174
  • 9
  • 74
  • 108