2
votes

In 6.2.6.1 p5, C11 says:

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.

and the 50th footnote is:

Thus, an automatic variable can be initialized to a trap representation without causing undefined behavior, but the value of the variable cannot be used until a proper value is stored in it.

In fact, I can understand neither of them.

3
Not a duplicate, but probably helpful: an answer to "what is a trap representation?"user149341

3 Answers

2
votes

The simplest type to take as an example that may have trap representations is _Bool. In fact it is only well defined if the value of a _Bool is 0 or 1. All other values are (or can be seen as) trap representations.

Now look at something like

struct both {
  _Bool b;
  char  c;
};

both two = { .c = 'a', };

printf("char: %c\n", two.c);                     // All fine
printf("bool: %s\n", two.b ? "true" : "false");  // error

So two.b has a value that is a trap and only if you try to use the value your program is on error, and then the behavior isn't defined anymore. E.g your compiler could decide to realize the choice of the two strings by something equivalent to

(char const*[]){ "false", "true" }[two.b]

and with the particular value, that isn't 0 or 1, your array access would be out of bounds.

1
votes

To start, a 'trap' value in an auto variable just means the contents of the variable are not valid for the type of the variable.

The 'unusable' statement just indicates that a variable that is not 'properly' initialized should not be used. Normally, the compiler will catch when the code tries to use an uninitialised variable and issue a warning.

This is yet another reason to always enable all the warnings when compiling, then fix those warnings.

1
votes

A trap representation means that the bytes that make up the object do not form a proper value of the type of the object. A typical trap representation for a 32-bit int * is 11 11 11 11: many implementations require (in the hardware itself, or in the optimisation passes of compilers) all int * to have a certain alignment, which is not met by these bytes. The undefined behaviour part means that if you try to read such an int * value anyway, things may break. The hardware may make the program crash, even if you don't attempt to dereference the pointer. The compiler may optimise alignment checks to make it look like the address is properly aligned -- because every int * is known to be aligned.

The footnote covers the fact that

void f() {
  { int i = 0x11111111; }
  { int *p; puts("Hello"); p = 0; }
}

is valid even if i and p are given the same address and 0x11111111 does not translate to a valid int *. At the call to puts, there is no undefined behaviour. If the hardware catches invalid values assigned to pointer registers, the compiler must simply make sure it does not load the p value into a register until it's been given a valid value.