Search code examples
cstructinitializationlanguage-lawyerundefined-behavior

Can I assign or return a struct with unassigned elements in C?


In C, using an uninitialized variable is undefined behavior. Including:

int x;
int y = x;

However, suppose I have the following:

struct Struct {
    int a;
    int b;
};
struct Struct s;
s.a = 1;
struct Struct s0 = s;
return s; // Assuming an enclosing function with the correct return type

Does the above code invoke undefined behavior, in the case that some, but not all, of the elements have been assigned to?


Solution

  • This is not adequately addressed by the C standard, but a reasonable C implementation supports using a whole structure when some members are uninitialized. And there is a simple workaround for any C implementation.

    Note that “In C, using an uninitialized variable is undefined behavior” is not entirely true. The value of an uninitialized object with automatic storage duration is indeterminate per C 2018 6.7.9 10. Using the value of such an object has undefined behavior if its address is not taken, per 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.

    Thus, to ensure using a structure does not have undefined behavior, it is merely necessary to take its address. Say you have some structure object s. Then we can take its address (and discard it) simply by putting (void) &s; in the code. By taking its address, we prevent the object from possibly being declared register, so 6.3.2.1 2 will not apply.

    There could still be some concern that using an object with an indeterminate value might encounter a trap representation. However, this does not occur for structures, per 6.2.6.1 2:

    … The value of a structure or union object is never a trap representation, even though the value of a member of the structure or union object may be a trap representation.

    That paragraph tells us the C committee intended C programs to be able to use whole structures even if their members were not all initialized. 6.3.2.1 2, about undefined behavior for some uninitialized objects was added later to provide for a Hewlett-Packard feature that allows the processor to track uninitialized registers. When that addition was made, the committee did not add a statement similar to 6.2.6.1 2 tell us that it would not apply to a structure.

    My sentiment would be that 6.2.6.1 2 indicates the intent is for it to be safe to copy whole structures and 6.3.2.1 2 is not intended to change that. However, to be entirely safe, taking the address of the structure as explained above ensures accessing the structure will not have undefined behavior regardless of one’s interpretation of the standard in this regard.