位域与联合体的内存布局
位域(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.