为什么使用 "designated initializer" 初始化 C 联合会给出随机值?

Why is initializing C union using "designated initializer" giving random values?

我有一个 "bug" 我花了很长时间追逐:

typedef union {
    struct {
        uint8_t mode: 1;
        uint8_t texture: 4;
        uint8_t blend_mode: 2;
    };
    uint8_t key;    
} RenderKey;

稍后将初始化此联合(在堆栈上):

Buffers buffers[128]; // initialized somewhere else

void Foo(int a, int b)
{
   //C99 style initialization (all the other values should be 0)
   RenderKey rkey = {.blend_mode = 1};      

   //rkey.key would sometimes be >= 128 thus would write out of array bounds
   DoStuffWithBuffer(&buffers[rkey.key]);
}

这似乎表明联合位域的最后一位不会被初始化。所以我通过添加未使用的位来修复它:

typedef union {
    struct {
        uint8_t mode: 1;
        uint8_t texture: 4;
        uint8_t blend_mode: 2;
        uint8_t unused: 1;
    };
    uint8_t key;    
} RenderKey;

这行得通,但我不明白为什么。 那个随机的 1 位来自之前堆栈上的随机垃圾,但为什么 C99 样式初始化在这里不起作用?因为 union 和匿名 struct?

这发生在 Clang 3.5tcc,但不会发生在 gcc 4.9.2

在 C11 中,§6.7.9 指出

The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject; all subobjects that are not initialized explicitly shall be initialized implicitly the same as objects that have static storage duration.

但是隐藏的填充位不是子对象,它不受约束,因为从匿名 struct 的角度来看它不存在,所以编译器没有初始化不存在的东西struct 的成员,毕竟不是那么奇怪。

一个类似的例子是

#include <stdio.h>

typedef struct {
    unsigned char foo;
    float value;
} Test;

int main(void) {
    Test test = { .foo = 'a', .value = 1.2f};

    printf("We expect 8 bytes: %zu\n", sizeof(Test));
    printf("We expect 0: %zu\n", (void*)&test.foo - (void*)&test);
    printf("We expect 4: %zu\n", (void*)&test.value - (void*)&test);

    unsigned char* test_ptr = (unsigned char*) &test;

    printf("value of 3rd byte: %d\n", test_ptr[2]);
}

test_ptr[2] 会是什么? struct 的两个成员之间有 3 个字节的填充,它们不属于任何子对象,初始化它们会浪费时间,因为在正常情况下您无法访问它们。