Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it legal to reference a variable in its definition?

int test[2] = { 45, test[0] };
int x = (x = 111);
cout << test[0] << " " << test[1] << " " << x << "\n"; // 45 45 111

Are the assignments in the first two lines legal? Visual Studio 2010 compiles and runs it without any errors or warnings but it seems like an odd case that could possibly be undefined, so I wanted to confirm that it is acceptable. Visual Studio does warn me if I do something blatantly reflexive (and presumably undefined) like int x = x; so I'm wondering how these situations it seems to allow are handled.

like image 569
0x5f3759df Avatar asked Nov 09 '12 00:11

0x5f3759df


2 Answers

From the C++ Standard (C++11, but it wasn't different in C++98/03):

(§ 3.3.2/1) The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any), [...] [ Example:

int x = 12;
{ int x = x; }

Here the second x is initialized with its own (indeterminate) value. —end example ]

This applies to user-defined types as well as array-types as well. Notice how the Standard emphasizes that x in the second example is initialised with an indeterminate value. So there is no way to know what value x is initialised with.

like image 139
jogojapan Avatar answered Oct 30 '22 14:10

jogojapan


I'll assume you're in some function since you're calling functions and such.

The space for both test and x is allocated on the stack. In theory, the space for these guys should exist before their values are filled in. If we look at the generated assembly (x86 gcc), this is true.

subl    $40, %esp         # Add 40 bytes of memory to the current stack
movl    $0, -20(%ebp)     # Clear test[0] to 0
movl    $0, -16(%ebp)     # Clear test[1] to 0
movl    $45, -20(%ebp)    # Place the value of 45 into test[0]
movl    -20(%ebp), %eax  # Copy that 45 into a register
movl    %eax, -16(%ebp)  # Move that register's value (45) into test[1]
movl    $111, -12(%ebp)  # Assign x to be 111, optimize out the unnecessary duplicate assignment
    ... #continues on to set up and call printf

We can see that 40 bytes are added to the stack. Notice how the addresses of test[0], test[1], and x are all contiguous addresses marked off from %ebp at 4 byte intervals (-20,-16,-12, respectively). Their locations in memory exist and can be accessed without error before they are defined. The compiler here clears them both to 0, although we can see that this is unnecessary. You can delete those two lines and still run fine.

What we can draw from this is that your int test[2] and int x could have any number of funky circular references within themselves and the code will compile - it's just your job to make sure your references grab good data (ie somehow initialized data) and not garbage, which you've done here. This also works with other cases - compile to assembly and check it out for yourself how it's done.

like image 1
GraphicsMuncher Avatar answered Oct 30 '22 14:10

GraphicsMuncher