3

This question is about the definedness or otherwise of assigning an uninitalised automatic variable to another one of the same type.

Consider

typedef struct
{
    int s1;
    int s2;
} Foo;

typedef union
{
    int u1;
    Foo u2; 
} Bar;

int main()
{
    {
        int a;
        int b = a; // (1)
    }
    {
        Foo a;
        Foo b = a; // (2)
    }
    {   
        Bar a;
        a.u1 = 0;
        Bar b = a; // (3)
    }
}

Referring to the comments in main:

(1) is undefined since a is uninitialised. That much I know.

But what about (2)? The struct members s1 and s2 are uninitialised.

Furthermore, what about (3)? The memory u2.s2 is uninitialised, so reading it is undefined behaviour no?

Ian Colton
  • 865
  • 2
  • 6
  • 7
  • Try `valgrind ./a.out` . you will see what you need to know. – Frank AK Nov 22 '17 at 09:29
  • I think there is *no* undefined behavior. But uninitialized data, yes. Therefore: yes, you can write those assignments, they are legal, they are useless. – linuxfan says Reinstate Monica Nov 22 '17 at 09:29
  • undefined behaviour because you cannot know the values (auto variables). but of course it's safe: it does nothing as long as you don't use them for something useful... – Jean-François Fabre Nov 22 '17 at 09:33
  • I think this 3rd example deserves the language-lawyer tag, so I'm adding it. I'm pretty sure it is well-defined as I wrote in my answer, but let's see. –  Nov 22 '17 at 10:04
  • related: https://stackoverflow.com/questions/45506400/consequences-of-uninitialised-variables-int-vs-unsigned-char/45507183#45507183 – bolov Nov 22 '17 at 11:09
  • I have followed up questions raised in this in [this new question](https://stackoverflow.com/questions/47433041/is-using-a-structure-without-all-members-assigned-undefined?noredirect=1#comment81820612_47433041). – Eric Postpischil Nov 22 '17 at 11:18

2 Answers2

4

The behavior is undefined in (1) and (2).

Per the C standard, the value of an object with automatic storage duration that is not initialized is indeterminate (C 2011 [N1570] 6.7.9 10). Nominally, this means it has some value, but we do not know what it is while writing the program.

However, the standard also says “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” (6.3.2.1 2). In your sample code, the address of a is never taken, and it is not initialized, and using it in an expression is an lvalue. Therefore, the behavior is undefined.

(This passage, 6.3.2.1 2, was designed to accommodate processors that can detect use of an uninitialized register. Nonetheless, the rule in the C standard applies to all implementations.)

(3) is not clearly addressed by the C standard. Although a member of the union has been assigned a value, and hence is not uninitialized for purposes of 6.3.2.1 2, the object being used in b = a is the union, not its member. Obviously, our intuitive notion is that, if a member of a union is assigned a value, the union has a value. However, I do not see this specified in the C standard.

We can infer 6.3.2.1 2 is not intended to consider a union or structure to be uninitialized, at least if part of it has been assigned a value, because:

  1. Structures can have unnamed members, such as unnamed bit fields.

  2. Per C 6.7.9 9, unnamed members of structures have indeterminate value, even after initialization (of the structures).

  3. If 6.3.2.1 2 applied to structures in which not every member had been assigned a value, then b = a would always be undefined if a were a structure with an unnamed member and had automatic storage duration.

  4. That seems unreasonable and not what the standard intended.

However, there is some wiggle room here. The standard could have specified that a structure is not uninitialized only if it were initialized or all of its named members have been assigned values. In that case (3) would be undefined if a were a structure in which only one member had been assigned a value. I do not think this wiggle room exists with a union; if a member of the union has been assigned a value, it is only reasonable to consider the union not to be uninitialized.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
3

In general, assigning from an uninitialized object isn't undefined behavior, it only makes the result unspecified.

But the code you show indeed has undefined behavior -- for a different reason than you assume. Citing N1570 (latest C11 draft), §6.3.2.1 p2 here:

[...] 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.

Explaining this a bit: The C standard is prepared to handle values that aren't stored in an addressable location. This is typically the case when they are held in one of the CPU's registers. Explicitly giving an object the register storage class is only a hint to the compiler that it should, if sensible, hold that object in a register. The other way around, a compiler is free to hold any object with automatic storage duration in a register as long as the code doesn't need to address it (by taking a pointer).

In your code, you have uninitialized objects with automatic storage duration that never have their address taken, so the compiler would be free to place them in registers. This means there is no value for the object (not even an unspecified one) before it is initialized. Therefore, using this potentially non-existent value to initialize another object (or, for other purposes) is undefined behavior.

If your code would take a pointer to the respective a in all these examples, the result of the assignment would be unspecified (of course), but the behavior would be defined.


It's worth to add that structs and unions have nothing to do with the answer to your question. The rules are the same for all kind of objects with automatic storage duration. That said, in your third example, a isn't uninitialized any more, after you assign one member of the union. So for your third example, the behavior is well-defined. It doesn't matter what's in the other member of the union, a union can only hold a value for one of its members at a time.

  • I am not sure 6.3.2.1 applies to (3). I do not see that the standard makes it clear what it means for a structure to be uninitialized in this situation. Clearly if all members of a structure were assigned a value, we would expect behavior to be defined, but this does not strictly satisfy the text in 6.3.2.1, as no assignment to the object that is the structure has been performed, just assignments to its members. But what if only some members have been assigned? Per 6.7.9 10, unnamed members of a structure have unspecified values even after initialization. So we can never give values to all… – Eric Postpischil Nov 22 '17 at 10:05
  • … members of a structure that has unnamed members. So, if `b = a` could be undefined if any member of `a` has not been initialized, then structures with unnamed members can never be used in expressions (unless you take their address). – Eric Postpischil Nov 22 '17 at 10:06
  • @EricPostpischil I completely forgot to consider that example **at all**, but edited a few minutes ago. It should be well-defined, the union is initialized after writing to one member. It would be even more interesting when asking about a struct. –  Nov 22 '17 at 10:06
  • Oops, I was thinking of (3) with a structure, not a union. (Which is still an interesting question.) Let me think about a union. – Eric Postpischil Nov 22 '17 at 10:07
  • @EricPostpischil I **think** the answer is the same for unions and structs, writing a single member is enough to have an initialized object -- but it's more obvious for unions given 6.2.6.1p7: "*When a value is stored in a member of an object of union type, the bytes of the object representation that do not correspond to that member but do correspond to other members take unspecified values.*" –  Nov 22 '17 at 10:11
  • @FelixPalmen The only bit I take issue with is one of the final comments "writing a single member is enough to have an initialized object". It's not writing to a single member that does it, it is *initializing* a single member that causes all remaining members to be treated as if they had been declared with static storage duration (and thereby initialized to zero). Cites. Start with `6.7.9 Initialization` then Notes `10` (aggregates are 3rd bullet), `12` (rest applies to aggregate or union) and finally `19` . – David C. Rankin Nov 22 '17 at 10:47
  • @DavidC.Rankin with a `union`, I think it doesn't matter given the passage I cited above. It would be interesting for a `struct`. I don't think initialization matters here, but merely the exact definition of when an object is considered *uninitialized*. The wording from 6.3.2.1 is just "*not declared with an initializer and no assignment to it has been performed prior to use*". If there's no better definition regarding aggregate types, I think this particular case might be underspecified. –  Nov 22 '17 at 10:52
  • 1
    I completely agree. Once you set one of the members of a union, you are good. The problem is aggregates (structs). That is where initialization of one member is required. (the reference to `12` with the word `union` in it is just how the conglomeration of all notes `12` and beyond applying to both struct and union. `19` is where the meat and potatoes are at `:)` – David C. Rankin Nov 22 '17 at 10:56