为什么这个联合结尾有char数组?

时间:2019-01-14 07:29:04

标签: c linux-kernel union

当我在阅读RCU锁原语的Linux内核实现时,最终遇到了以下代码。

#define WRITE_ONCE(x, val) \
({                                                      \
        union { typeof(x) __val; char __c[1]; } __u =   \
                { .__val = (__force typeof(x)) (val) }; \
        __write_once_size(&(x), __u.__c, sizeof(x));    \
        __u.__val;                                      \
})


static __always_inline void __write_once_size(volatile void *p, void *res, int size)
{
        switch (size) {
        case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
        case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
        case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
        case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
        default:
                barrier();
                __builtin_memcpy((void *)p, (const void *)res, size);
                barrier();
        }
}

据我所知,以以联合的前一部分大小的char数组结尾的联合允许您访问按字节粒度分配给联合的内存。

例如

#define UNION_PARTIAL \
    int a;            \
    int b;            \
    char *c;          \

union union_partial {
    UNION_PARTIAL
};

union union_full {
    UNION_PARTIAL
    char bytes[sizeof(union union_partial)];
};

但是,似乎未声明在WRITE_ONCE宏中使用的联合会提供对联合内存的精细访问。可以改用下面的代码,但是我不知道为什么我们需要char __c [1]。

#define WRITE_ONCE(x, val) \
({                                                      \
        union { typeof(x) __val; char __c[1]; } __u =   \
                { .__val = (__force typeof(x)) (val) }; \
        __write_once_size(&(x), &(__u.val), sizeof(x)); \
        __u.__val;                                      \
})

仅仅是因为减轻了程序员在__u.val前面键入的负担?

1 个答案:

答案 0 :(得分:1)

让我们谈谈READ_ONCE()。如果将const指针传递给采用void *指针的函数,C编译器将抱怨。 __read_once_size被声明为static __always_inline void __read_once_size(volatile void *p, void *res, int size)。该宏声明为:

#define READ_ONCE(x) \
    ({ typeof(x) __val; __read_once_size(&x, &__val, sizeof(__val)); __val; })
我认为,当typedef中包含const时,

会引起警告。

typedef const int type_t;
struct a_s {
   type_t m;
};
struct a_s a = { .m = 1; };
type_t b = READ_ONCE(a.m);

在这种用法中,typeof(x)const int,所以&__valconst int*,当转换为void*时会引起警告/错误。在这种用法中,const不会被抛弃在typeof上,因此我们将const *指针传递给__write_once_size函数。因此,作者决定使用“联合技巧”,通过将指针传递到与值相同的位置处的数组来摆脱常量性。 (一个人也可以进行一些奇怪的强制转换(const void*)(uintptr_t)(void*),但它并不那么可移植)。

this commit中的作者对此进行了解释,并更改了READ_ONCE宏:

- ({ typeof(x) __val; __read_once_size(&x, &__val, sizeof(__val)); __val; })
+ ({ union { typeof(x) __val; char __c[1]; } __u; __read_once_size(&(x), __u.__c, sizeof(x)); __u.__val; })

为了保持一致并适应READ_ONCE()的更改,WRITE_ONCE()宏在this commit中进行了相应的更改。