0
int main() {
 char a[5];
 a[0] = 0;
 return a[0];
}

In this snippet, does char a[5];, which is an array declaration without any initialization, guarantee allocation of 5 bytes of consecutive memory?

Or since only 1 element gets initialized later, compiler is free to use a register for that 1 element?

I'm assuming that reading any other index of this array beside 0 is undefined behaviour.

Dan
  • 2,694
  • 1
  • 6
  • 19

1 Answers1

5

It guarantees that the observable behaviour of the program will be the same as if it has allocated this memory.

The compiler is free to optimize out any objects as in your trivial example:

main:
        xor     eax, eax
        ret

https://godbolt.org/z/KE5PzvTKq

0___________
  • 60,014
  • 4
  • 34
  • 74
  • Thank you. I also wanted to confirm, reading any other index here beside `0` is considered undefined behaviour correct? unless the entire array in 0 initialized in the beginning like `char a[5] = {0};` ? – Dan Jun 13 '21 at 23:04
  • 2
    @Dan yes --------- – 0___________ Jun 13 '21 at 23:05
  • 1
    No, reading uninitialized elements of the array will produce “indeterminate” values. There is a rule in the C standard that reading uninitialized values may have undefined behavior, but it applies only to objects that could have been declared with `register` (and that have automatic storage duration). Array elements can never be declared with `register`, so the rule does not apply. – Eric Postpischil Jun 13 '21 at 23:25
  • @EricPostpischil is this a C and C++ rule or only C? links with more details on this? – Dan Jun 13 '21 at 23:42
  • This link says they are undefined in C++: https://stackoverflow.com/questions/49696810/in-c-is-accessing-an-uninitialized-array-unspecified-behavior-or-undefined-be – Dan Jun 13 '21 at 23:57
  • The rule in C is C 2018 6.3.2.1 2: “… If the lvalue designates an object of automatic storage duration that could have been declared with the `register` storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.” – Eric Postpischil Jun 14 '21 at 00:18
  • So the the link i posted is wrong? or c++ could be different? I can post a separate question on this. – Dan Jun 14 '21 at 00:20
  • For C++, the current top (and only positive) answer at the question you link to says there are exceptions, although it does not list them (it truncated the quoted text). Referring to the full text in a draft of the C++ standard, the exceptions are largely placeholders—initializing an `unsigned char` with an indeterminate `unsigned char` yields an indeterminate value, and so on—and these just kick the can down the road until, at some point, the indeterminate value is just discarded (is not used for anything) or is used for something outside the exceptions and then has undefined behavior. – Eric Postpischil Jun 14 '21 at 00:20
  • @EricPostpischil Arrays can be declared with `register` storage class; I would interpret the clause you are talking about to also apply to elements of such arrays. This fits with the rationale of the rule, that registers may generate traps -- e.g. `int x[1]; y = x[0];` might be stored in the same way as `int x; int y = x;` and therefore have the same hardware trap issue – M.M Jun 14 '21 at 00:23
  • @M.M: Arrays “can” be declared with `register` storage class but they are unusable: C 2018 6.3.2.1 3, which specifies the automatic conversion of arrays to pointers, says “If the array object has register storage class, the behavior is undefined.” The parenthesized text in 6.3.2.1 2, “never had its address taken”, makes it clear that this criterion about declaring with `register` is a proxy for stating the compiler has not been forced to give an address for the object. And array elements fail this; `a[i]` is, by definition `(*((a)+(i)))`, so the element’s address is `(a)+(i)`. – Eric Postpischil Jun 14 '21 at 00:51
  • @M.M: See C 2018 note 128: “… the address of any part of an object declared with storage-class specifier `register` cannot be computed, either explicitly (by use of the unary `&` operator as discussed in 6.5.3.2) or implicitly (by converting an array name to a pointer as discussed in 6.3.2.1). Thus, the only operator that can be applied to an array declared with storage-class specifier register is `sizeof`.” Thus, if we declare `char x[1];` and write `x[0]`, this note confirms we would be computing the address of `x[0]` and that we “cannot” do that. – Eric Postpischil Jun 14 '21 at 00:56
  • @M.M: A note about “can”, “could”, and “cannot”: Of course we **can** write the code `char x[1]; x[0];`, and some compilers may let us compile it and execute it, but, in these passages, the C standard uses “can” and “cannot” in regard to using C in a defined way, not as a matter of physical possibility or lenience by a compiler. Thus, for the purposes of 6.3.2.1 2, an array does not satisfy “could have been declared with the `register` storage class”. – Eric Postpischil Jun 14 '21 at 00:58
  • @EricPostpischil I don't see how your preamble is supposed to support your last sentence. My claim is that "could have been declared with the `register` storage class" for an array element refers to the declaration which created that element; and this claim is based on the rationale for the rule . – M.M Jun 14 '21 at 01:44
  • @EricPostpischil i have posted this https://stackoverflow.com/questions/67964624/is-reading-uninitialized-array-elements-undefined-behaviour-in-c – Dan Jun 14 '21 at 04:29
  • Correction: The note I referred to is note 124, at 6.7.1 6, not 128. – Eric Postpischil Jun 14 '21 at 10:23