17

Possible Duplicate:
array initialization, is referencing a previous element ok?

I wonder if its safe to do such initialization in c/c++ standard:

int a = 5;
int tab[] = { a , tab[0] + 1 , tab[1] };

It successfully compiles and executes with gcc 4.5 and clang 2.9, but will it always be true?


Printing this table gives 5 6 6. Its initialized in global scope.


Generally its interesting in both, c and c++, but i want to use it in c++:)

8
  • 6
    @一二三: not really no. Commented Sep 11, 2011 at 13:32
  • 1
    why not just write int tab[] = {a, a+1, a+1}? I don't get the point. Commented Sep 11, 2011 at 13:34
  • 1
    @Tomalak Whereas I know that there is no C/C++, it would still be nice to know what both standards say about this. Commented Sep 11, 2011 at 13:35
  • @Andreas The question is surely not how to best accomplish this or how you can do it alternatively, but if this is UB or not. And this is IMHO a very interesting question which I myself would like to be answered. Commented Sep 11, 2011 at 13:37
  • @Andreas: The point is to know the rules for referring to previous element initialisers later on in a list-initialiser. Seems pretty clear. Commented Sep 11, 2011 at 13:37

5 Answers 5

7

C++03/C++11 answer


No, it won't.

On the right-hand side of the =, tab exists1 but — if it has automatic storage duration — it has not yet been initialised so your use of tab[0] and tab[1] is using an uninitialised variable.

If tab is at namespace scope (and thus has static storage duration and has been zero-initialized), then this is "safe" but your use of tab[0] there is not going to give you 5.

It's difficult to provide standard references for this, other than to say that there is nothing in 8.5 "Initializers" that explicitly makes this possible, and rules elsewhere fill in the rest.


1 [n3290: 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) [..]

Sign up to request clarification or add additional context in comments.

13 Comments

With g++ and clang result is 5 6 6, so what i expected.
@qba: Read my answer again. You can't guarantee this result.
Is it really "safe"? Doesn't the dynamic initialization mean that the object is written to, so doesn't reading it to calculate the value to be stored in a different array element cause UB because neither operation is sequenced before the other?
@Charles: It's safe to read from a static before you initialised it, because it was already zero-initialised. Unless you're referring to that mechanism...?
[n3290: 3.6.2/2]: Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place. [..]
|
5
int a =5;
int tab[] = { a , tab[0] + 1 , tab[1] };

If these variables are declared at namespace scope, then they're okay, as at namespace scope variables are zero-initialized (because of static initialization - read this for detail).

But if they're declared at function scope, then second line invokes undefined behaviour, since the local variables are not statically initialized, that means, tab[0] and tab[1] are uninitialized, which you use to initialize the array. Reading uninitialized variables invokes undefined behavior.

2 Comments

So you will get a 0 instead of a 5 for tab[0]?
@Tomalak Interesting that you answered this comment, as I saw your answer and then thought about the incompleteness of this one.
4

In the C99 standard, it seems that the order of initialization of the members is guaranteed:

§6.7.8/17: Each brace-enclosed initializer list has an associated current object. When no designations are present, subobjects of the current object are initialized in order according to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union. In contrast, a designation causes the following initializer to begin initialization of the subobject described by the designator. Initialization then continues forward in order, beginning with the next subobject after that described by the designator.

But as @Tomalak mentions in the comment, that does not provide a full guarantee for the operation, as the compiler would first evaluate all of the arguments and then apply the results in the previous order. That is, the previous quote does not impose an order between the initialization of tab[0] and the evaluation of the expression tab[0]+1 that is used to initialize tab[1] (it only imposes an ordering between the initialization of tab[0] and tab[1])

As of the C++ standard, neither in the current standard nor the FDIS of the upcoming C++0x standard seem to have an specific clause defining the order in which the initialization is performed. The only mention of ordering comes from

§8.5.1/2 When an aggregate is initialized the initializer can contain an initializer-clause consisting of a brace-enclosed, comma-separated list of initializer-clauses for the members of the aggregate, written in increasing subscript or member order.

But that only relates to the order by which the entries in the initializer are written, not how it is actually evaluated.

1 Comment

The C99 quote doesn't guarantee that that operand evaluation occurs within this sequential initialisation process.
4

so, now I've run a couple of tests regarding your problem.

All compilations have been performed with your example code above, using the following scheme:

$(GCC) -o a.out test.c -Wall -Wextra -pedantic -std=$(STD)

This yielded the following results:

for GCC = gcc, the standards -std=c89; -std=iso9899:1990; -std=iso9899:199409; -std=gnu89 resulted in a warning showing up: initializer element is not computable at load time and undefined behaviour at runtime, meaning that the second and third value of the array were random garbage.

the standards -std=c99; std=iso9899:1999; -std=gnu99 did not produce this warning, but also showed undefined behaviour at runtime.

for GCC = g++, the standards -std=c++98; -std=gnu++98; -std=c++0x produced no warning, and the code worked as you'd expected it to, resulting in an array containing the values {5, 6, 6}.

However, as most of the people advised, it might be unwise to use this, since your code might behave differently on other compilers, or maybe even other versions of the same compiler, which is generally a bad thing :)

hope that helped.

6 Comments

How did you manage to detect undefined behaviour, pray tell?
BTW it's not just about "other compilers, or maybe even other versions of the same compiler", but even different runs with the same compiler; or perhaps it only appeared to work in your case but in fact didn't at all and instead created a finger-sized hole in the universe..?
@Tomalak I printf()'d the contents of the array. that's a very reliable way to detect UD imo. And I was only stating experimental results, without any claim of completeness. If you find an example of g++ creating an executable where the declaration of the question above does not produce the intended results, let me know :) - My guess is, that the g++ detects initializations of this kind and handles them appropriately. But, as I said, I wouldn't rely on it, and would most certainly never use it.
It is mathematically impossible, in many cases, to "detect" UB. Certainly, printf-ing something is never a UB detection tool.
well, of course exact detection is generally not possible. But the case we have here is pretty simple, in a way that allows to identify undefined behaviour if something went wrong and all other possible sources for "something wrong" have been eliminated. The probability that it's not UB when something went wrong is insignificant, and can thus be eliminated almost completely by running every test at least twice.
|
0

Yes - It will probably work as you expect it to be.
No - (you didn't ask but) Don't use it, it make no logic and it's a bad practice.

1 Comment

Define "probably". "No" is the correct answer here; don't sugarcoat it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.