位域(bit field)与联合体(union)都可以用来控制内存布局。本文主要阐述的内存可总结为:位域的内存布局是编译器决定的,而联合体的内存布局是由大小端决定的。

位域的内存布局

观察以下代码段,思考程序可能的输出内容。

#include <stdio.h>
union {
    struct {
	unsigned char c1 : 2;
	unsigned char c2 : 3;
	unsigned char c3 : 3;
    } s1;
    unsigned char s2;
} u1;

int main(int argc, char* argv[]) {
    printf("sizeof u1 = %d\n", sizeof(u1));

    u1.s2 = 100;

    printf("u1.s1.c1  = %d\n", u1.s1.c1);
    printf("u1.s1.c2  = %d\n", u1.s1.c2);
    printf("u1.s1.c3  = %d\n", u1.s1.c3);

    printf("u1.s2     = %d\n", u1.s2);

    return 0;
}

正确答案是,这段代码在不同编译器下的表现是不同的。

位域在各个编译器下是不一样的,并且它不是便携的(portable)。即使在相同平台上,不同编译器的内存布局也可能不一样。所以在使用位域时要格外小心。

以下引用摘自C99标准。

The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined.

结构体的内存布局

观察以下代码段,思考程序可能的输出内容。

#include <stdio.h>

typedef union un {
    int x;
    char ch[4];
}un;

int main(int argc, char* argv[]) {
    un u;
    u.ch[0] = 1;
    u.ch[1] = 2;
    u.ch[2] = 3;
    u.ch[3] = 4;

    printf("u.x=%d\n", u.x);
    printf("u.x=0x%x\n", u.x);

    return 0;
}

正确的答案是,输出的结果与平台的大小端有关。

typedef union epoll_data {
  void        *ptr;
  int          fd;
  __uint32_t   u32;
  __uint64_t   u64;
} epoll_data_t;

对于以上代码,联合体中的成员具有相同的地址,即使它们的大小不同。

以下引用自C99标准。

The size of a union is sufficient to contain the largest of its members. The value of at most one of the members can be stored in a union object at any time. A pointer to a union object, suitably converted, points to each of its members (or if a member is a bit-field, then to the unit in which it resides), and vice versa.