6

This question is inspired by answers to this question.

Following code has potential for undefined behaviour:

uint64_t arr[1]; // Uninitialized
if(arr[0] == 0) {

C standard specifies that uninitialized variable with automatic storage duration has indeterminate value, which is either unspecified or trap representation. It also specifies that uintN_t types have no padding bits, and size and range of values are well defined; so trap representation for uint64_t is not possible.

So I conclude that uninitialized value itself is not undefined behavior. What about reading it?

6.3.2.1 Lvalues, arrays, and function designators

  1. ...
  2. Except when it is the operand of the sizeof operator, the _Alignof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion. ... -- irrelevant text removed -- ... 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.
  3. Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

Question: Does subscripting array count as taking the address of an object?

Following text seems to imply that subscripting array requires conversion to a pointer, which seems impossible to do without taking address:

6.5.2.1 Array subscripting

Constraints

  1. One of the expressions shall have type ‘‘pointer to complete object type’’, the other expression shall have integer type, and the result has type ‘‘type’’.

Semantics

  1. A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).

This makes §6.3.2.1 paragraph 3 seem weird. How could array have register storage class at all, if subscription requires conversion to a pointer?

user694733
  • 15,208
  • 2
  • 42
  • 68
  • This is a duplicate of https://stackoverflow.com/q/11962457/4389800 (for Q1) and https://stackoverflow.com/q/17342881/4389800 (for Q2). – P.P Dec 19 '17 at 10:15
  • 1
    [footnote 121](http://port70.net/~nsz/c/c11/n1570.html#note121) says that the only allowed operations for an array declared with register storage class are `sizeof` and `_Alignof`. I.e. not useful at all. Naturally, otherwise the behaviour is just *undefined*, it doesn't mean that an implementation couldn't supply some reasonable and usable behaviour for that case. – Antti Haapala -- Слава Україні Dec 19 '17 at 10:47
  • @P.P. I have to disagree. First seems to cover aspects I already knew, and I feel the second doesn't cover language lawyering at all from this questions perspective. – user694733 Dec 19 '17 at 10:48
  • @AnttiHaapala That's an excellent find. That would mean that `arr` cannot be placed in register. – user694733 Dec 19 '17 at 10:55

1 Answers1

6

Yes, array subscripting counts as taking the address, as per the part you quoted in 6.5.2.1. The expression E1 must have its address taken.

Therefore the special case of UB in 6.3.2.1 does not apply to array indexing. If array indices are used, it is not relevant if the array could be stored with register storage duration or not (a variable having its address taken cannot use register storage duration).

You are correct in assuming that reading an uninitialized stdint.h type with indeterminate value, which has its address taken, does not invoke undefined behavior (guaranteed by C11 7.20.1.1), but merely unspecified behavior. The value could be anything and it can be non-deterministic between several reads, but it cannot be a trap.

"Reading an uninitalized variable is always UB" is a wide-spread but incorrect myth. Further information with normative sources in this answer.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • I'd quote [this footnote](http://port70.net/~nsz/c/c11/n1570.html#note121)... i.e. any array that is used for anything else besides `sizeof` or `_Alignof` cannot fulfil the "could be stored with register storage duration" – Antti Haapala -- Слава Україні Dec 19 '17 at 10:46
  • I don't see how 7.20.1.1 precludes a trap representation. There could be attributes of memory that are not visible to the C program, such as a checksum bit. – Andrew Henle Dec 19 '17 at 10:47
  • @AnttiHaapala In the general case, yes, as long as the array does not use indexing (which is kind of the whole point of having an array). But it would of course make sense to store for example an array of 4 bytes in a 32 bit register, rather than on the stack. – Lundin Dec 19 '17 at 11:54
  • @AndrewHenle "no padding bits, and a two’s complement representation". The variable's value uses _all_ data bits, there is no room for anything else. A trap representation in this sense is a software trap such as SIGSEGV or SIGFPE. While memories and hardware are outside the scope of the programming language standard. If the hardware memory has some aspect such as preventing data reads from certain memory, then that yields a hardware exception, which is not regulated by the programming language standard. – Lundin Dec 19 '17 at 12:00
  • Please note that the C standard's definition of a trap is: "_an object representation_ that need not represent a value of the object type". Meaning that mysterious hardware aspects that are not part of the object representation have nothing to do with trap representations, as they are specified by the C standard. – Lundin Dec 19 '17 at 12:05
  • @EricPostpischil https://stackoverflow.com/questions/17394907/c-is-an-indeterminate-value-indeterminable. And some other DR after that too, I don't remember the number. – Lundin Dec 19 '17 at 12:56
  • @EricPostpischil No, it is correct. Feel free to post an answer of your own. – Lundin Dec 19 '17 at 13:01
  • 1
    @EricPostpischil read the link from my first comment. Also, the defect report [451](http://www.open-std.org/jtc1/sc22/wg14/www/docs/summary.htm#dr_451) – Antti Haapala -- Слава Україні Dec 19 '17 at 14:02
  • Also note C 2018 note 124 (at 6.7.1 6): “… 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, in the sense the C standard uses “can” and “could have”, when we use `arr[0]`, then the array `arr` could not have been declared `register`. – Eric Postpischil Jun 14 '21 at 10:20