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



我有一个"错误",我花了很长时间追逐:

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中说

初始化应在初始化列表顺序中发生,每个初始化器为特定的子对象提供了相同子对象的任何先前列出的初始化器;所有未明确初始化的应与具有静态存储持续时间的对象相同。

但是隐藏的填充位不是一个子对象,它不会受到约束,因为从匿名的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: %zun", sizeof(Test));
    printf("We expect 0: %zun", (void*)&test.foo - (void*)&test);
    printf("We expect 4: %zun", (void*)&test.value - (void*)&test);
    unsigned char* test_ptr = (unsigned char*) &test;
    printf("value of 3rd byte: %dn", test_ptr[2]);
}

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

最新更新