分类: C/C++
2012-04-25 12:59:32
C里的所有对象都必须表示成连续的字节序列,且每个都至少有8位的宽度。在一个36位字长的机器上,一个字节可以定义成9、12、18、或36位, 对于占用一个字节的字符类型来说,它的位数也不会小于8。sizeof返回的“字节”数也是与机器无关的,它可能是8位、9位或其它的位数。
因为这种宽度限制,所以空结构体的对象也要占用至少一字节的空间。比如:
struct Empty
{
};
printf("%d, ", sizeof(struct Empty));
struct Empty e1, e2;
printf("%p, %p\n", &e1, &e2);
输出为:0, 0xbf9ccf8e, 0xbf9ccf8f
再讨论下包含空结构体对象的结构体:
struct EmptyGroup
{
struct Empty e1;
struct Empty e2;
struct Empty e3;
};
int main()
{
struct EmptyGroup eg1, eg2;
printf("%p, %p\n", &eg1, &eg2);
printf("%p, %p, %p\n", &(eg1.e1), &(eg1.e2), &(eg1.e3));
return 0;
}
输出为:
0xbff7965e, 0xbff7965f
0xbff7965e, 0xbff7965e, 0xbff7965e
可以看到各成员共用一个地址。
注意,如果是C++,它的输出会有不同:
0xbfca68d8, 0xbfca68db
0xbfca68d8, 0xbfca68d9, 0xbfca68da
每个成员对象都占用了一个字节,这种特性在C++中保证了多重继承的可能性。
结构体的内存布局有一些限制:
1、结构体开头不能有空洞(hole);
2、成员会增加存储空间;
3、在结构体末,需要的时候可能会放置一个空洞来使结构体足够大,以便在数组里紧密排列并有恰当的内存对齐。
可变长度的结构体
在C89,有一种称为“struct hack”的方法来得到可变数组。
struct s
{
int n_items;
int items[1];
};
struct s *p = malloc(sizeof(struct s) + (n - 1) * sizeof(int));
这种方式可以使得结构体里的数组长度为n。但它被认为多少有些邪恶的。C99提供了类似的但合法的“struct”机制:
struct OK
{
int n;
int a[];
};
struct OK *p = (struct OK *)malloc(sizeof(struct OK) + 10 * sizeof(int));
p->a[9] = 9;
注意只有结构体内最后的数组才可以有可变长度,且它不能是唯一的成员。
结构体的初始化与赋值
C89的扩展和C++都支持“Compound Literal”的概念。例如:
struct R
{
int c;
double d;
};
struct S
{
int a;
float b;
struct R r;
char *s;
};
结构体的初始化和赋值可以很简明:
struct S s = { 1, 3.4, {6, 7.8}, "hello" };
s = (struct S){ 1, 3.4, {6, 7.8}, "hello" };
s.r = (struct R){6, 7.8};
C99提供了“Designated Initializers”的机制,它可以以任意顺序为结构体赋值:
s.r = (struct R) { .c = 8, .d = 9.0 };
或 s.r = (struct R) { .d = 8.0, .c = 9 };
对于union,只能用Designated Initializers,比如:
u = { .a = 4 };
虽然Compound Literal也可以使用,但它的效果仅等同于cast,结果可能会不正确。