0
votes

Say I have defined an unsigned char foo;, which is guaranteed by the standard not to have trap representations. According to this answer, accessing it before its address is taken is still undefined behaviour. Here is the reference from N1570, "6.3.2.1 Lvalues, arrays, and function designators" paragraph 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.

However, I wonder whether volatile unsigned char foo; invokes undefined behaviour? It seems impossible for a volatile-qualified variable to be stored in a register.


Also, is the following code well-defined?

unsigned char foo;
*&foo -= *&foo;

This question comes from the 102rd footnote:

102) ...... *&E is a function designator or an lvalue equal to E. ......

1
w.r.t volatile, register and "stored in a register" are completely unrelated concepts in C, and one doesn't imply the other. 6.7.3: "An object that has volatile-qualified type may be modified in ways unknown to the implementation .. What constitutes an access to an object that has volatile-qualified type is implementation-defined." i.e. the point of volatile is to go beyond standard-defined behaviour. - Leushenko
"accessing it before its address is taken" - Where did you get that from? There is no requirement for a variable to even have an address unless its address is used (e.g. the & operator is applied). And *& is identical to not applying any of them, read the whole footnote 102. - too honest for this site
@Olaf But the standards just writes "... that could have been declared with the register storage class (never had its address taken), ...", and the use of the address taken is not required - nalzok
register just prevents the address from being taken, thus you cannot apply & to it. It would not make any sense to have a auto volatile register variable. That could not be changed externally (no linkage, etc.) and the compiler even might optimize it out if it can prove it will have no effect (reason why delay-loops with such code might be problematic on a very aggressive compiler). Actually, auto volatile alone is already quite useless. - too honest for this site
@Olaf You mean a auto volatile register qualifier/specifier is nearly equivalent to volatile, and the only difference is that the address of the variable defined cannot be taken? - nalzok

1 Answers

3
votes

First to clarify, the quoted part from 6.3.2.1 addresses a special case, where you have an uninitialized variable which is not necessarily allocated at an address, because the address is never taken. Accessing such an object is explicitly undefined behavior. This is what the linked answer is concerned about.

If that special case does not apply, because the address is taken, then relvant parts would be 6.7.9/10:

If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate.

The definition of indeterminate value 3.19.2:

either an unspecified value or a trap representation

The definition of unspecified value 3.19.3:

valid value of the relevant type where this International Standard imposes no requirements on which value is chosen in any instance

NOTE An unspecified value cannot be a trap representation.

And finally the part that says that accessing a trap representation is UB, 6.2.6.1/5, emphasis mine:

Certain object representations need not represent a value of the object type. If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined. If such a representation is produced by a side effect that modifies all or any part of the object by an lvalue expression that does not have character type, the behavior is undefined.50) Such a representation is called a trap representation.

To sum it up, an indeterminate value is not necessarily a trap representation and therefore, accessing an uninitialized variable is not necessarily undefined behavior.


There is, as far as I know, nothing in the standard explicitly saying that an unsigned character cannot hold a trap representation. 6.2.6.2 only says that they cannot have padding bits. Though in practice this would mean that an unsigned char cannot hold a trap representation, because in order to do so it would either need padding bits or a signed format. So between the lines, I think it is safe to assume that unsigned char cannot hold a trap representation.


However, I wonder whether volatile unsigned char foo; invokes undefined behaviour?

Declaring a variable never invokes undefined behavior in itself. If you access the variable without initializing it, then in the normal case, the value would be indeterminate. And it is implementation-dependent if this would be a trap representation or not.

However, volatile is a special case and none of that applies. Instead you have 6.7.3/7: "... What constitutes an access to an object that has volatile-qualified type is implementation-defined."

So it is simply implementation-defined behavior. Regardless of the type.


Also, is the following code well-defined?

unsigned char foo;
*&foo -= *&foo;

foo has an indeterminate value. It's address is taken. So whether or not this is undefined behavior depends on if the indeterminate value is a trap representation or not, on the given system. As discussed above, I don't think an unsigned char can ever be a trap representation.

However you used the -= operator which would make this expression equivalent to

*&foo = *&foo - *&foo; 

The binary - operator promotes both operands to int during calculation. If there were no trap representations to begin with, there could now be some in the promoted operands, which are of signed type, possibly with padding bits.

Meaning that this particular expression could invoke undefined behavior.