19

I accidentally created a bug in a program by self-referencing in an array. Here's a very simplified demo program similar in concept:

#include <iostream>
using namespace std;

int kTest[] = {
    kTest[0]
};

int main() {
    cout << kTest[0] << endl;
}

I was surprised that I received neither a compiler error or even a warning on this code! In my case it ended up producing unpredictable output. Is it accessing garbage memory?

I was curious about under what circumstances this would have well-defined output (if ever!).

Edit: Does it make a difference if kTest is static? What about const? Both?

aardvarkk
  • 14,955
  • 7
  • 67
  • 96
  • hmm. global `int`s are zero initialized so I wonder if because of that it is legal. – NathanOliver Oct 19 '16 at 17:52
  • Under the circumstances in which the language standard allowed this sort of syntax. – barak manos Oct 19 '16 at 17:52
  • 2
    It's not very different from [`int x = x;`](http://stackoverflow.com/questions/14935722/does-initialization-entail-lvalue-to-rvalue-conversion-is-int-x-x-ub). – Kerrek SB Oct 19 '16 at 17:54
  • 1
    Why would it be an error? `kTest` has been declared by the point of use. Globals are _normally_ initialized to 0, but since you are _manually_ initializing it, it initializes with the zeroth element --- which is garbage since it's still in the process of initializing. – Paul J. Lucas Oct 19 '16 at 17:54
  • @PaulJ.Lucas: Unfortunately, the C++ standard has no notion of "garbage" :-S – Kerrek SB Oct 19 '16 at 17:55
  • 4
    Is this actually the real code that produces the alleged "garbage"? Since this initialization is dynamic (given that the initializer is not a constant expression), `kTest` is guaranteed to be zero-initialized in the static phase. – Kerrek SB Oct 19 '16 at 17:58
  • The real fun begins when you make `kTest` into a `constexpr`. Then the magic UB-detector that is `constexpr` kicks in. – Kerrek SB Oct 19 '16 at 18:00
  • 2
    I think that this is formally UB only if the index in `kTest[...]` is equal to or larger than the number of entries in `kTest` (for example, if you used `kTest[5]` up there, without adding at least 5 more entries). – barak manos Oct 19 '16 at 18:05
  • @KerrekSB No, this isn't the exact same code. It actually accessed another array at an *index* specified by the self-reference. And it was declared `static const`. I didn't think that stuff was overly relevant but maybe it was. – aardvarkk Oct 19 '16 at 18:59
  • @KerrekSB note [it is ill-formed](http://stackoverflow.com/q/34276373/1708801) not UB so slightly different. – Shafik Yaghmour Oct 23 '16 at 04:45
  • @ShafikYaghmour: I see, yes, but where does the ill-formedness come from? The issue resolution doesn't spell that out, it just seems kind of implied. – Kerrek SB Oct 23 '16 at 12:15
  • @ShafikYaghmour: I thought the ill-formedness you mention would be a consequence of [expr.const]/(2.6), because it *would* be UB to read an uninitialized variable. – Kerrek SB Oct 23 '16 at 12:21

2 Answers2

15
int kTest[] = {
    kTest[0]
};

is similar to, if not exactly same as

int x = x;

It will be undefined behavior, if declared locally in a function.

It seems to be well defined when kTest is a global variable. See the other answer for additional details.

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 1
    This should be more than an opinion shouldn't it? Or is whether or it's UB not defined by the standard? – Carcigenicate Oct 19 '16 at 18:07
  • Could it be unspecified? I guess it is similar to this: `int x; x = x;`. – Aaron McDaid Oct 19 '16 at 18:14
  • @Carcigenicate, I see in the C++11 standard where `int x = x;` is discussed but I can't find anything that talks about the OP's situation. – R Sahu Oct 19 '16 at 18:17
  • I am not so sure, if you look at [Self-initialization of a static constexpr variable, is it well-formed?](http://stackoverflow.com/q/34276373/1708801) as far as I can tell this should zero initialized. – Shafik Yaghmour Oct 23 '16 at 05:13
8

I'm not so sure this is undefined. Quote from the current draft:

[basic.start.static]/3

If constant initialization is not performed, a variable with static storage duration ([basic.stc.static]) or thread storage duration ([basic.stc.thread]) is zero-initialized ([dcl.init]). Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place.

To me it looks like kTest is already zero-initialized when the dynamic initialization starts, so it may be defined to initialize to 0.

krzaq
  • 16,240
  • 4
  • 46
  • 61
  • Your interpretation makes sense.`g++` did not complain about `int x = x;` when defined globally but complained when defined in a function. – R Sahu Oct 19 '16 at 18:34
  • 1
    Why do you assume that the OP's global initialization is dynamic? If I have `static int a[] = { 1 };` that is initialized statically only. `a[0]` is not initialized to 0 (statically) and then initialized again to 1 (dynamically). – Paul J. Lucas Oct 19 '16 at 19:44
  • @PaulJ.Lucas: *Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization.* – krzaq Oct 19 '16 at 19:45
  • 1
    @Paul J. Lucas: Becuase it is not zero-initialization and it does not satisfy requirements of constant-initialization. That immediately makes it dynamic. Your `static int a[] = { 1 };` example is completely different. However, conceptually it **is** actually processed in two stages: as zero-initialization followed by initialization with 1. Both stages are *static* initialization though. It is C++17 that decieded to get rid of the unnecessary zero-initializtion when constant-initialization is present. – AnT stands with Russia Oct 19 '16 at 19:55
  • See [my comment](http://stackoverflow.com/questions/40138325/c-self-referencing-array#comment67664922_40138406) I believe your interpretation is correct. – Shafik Yaghmour Oct 23 '16 at 05:14