求解析C语言结构体大小及代码各输出结果的原因
先贴出原代码:
struct X {struct X *p; short a, *b;}; struct X x, *p=&x; printf("0:%x\n", p); printf("1:%x\n", sizeof(*p)); printf("2:%x\n", &p->a); printf("3:%x\n", &p->b); printf("4:%x\n", ++p); printf("5:%x\n", p-&x); printf("6:%x\n", &x-p);
对应的输出(根据你提供的图片整理):
0:7ffeefbff5e0 1:18 2:7ffeefbff5e8 3:7ffeefbff5ea 4:7ffeefbff5f2 5:1 6:ffffffff
下面逐个解释每一行输出的逻辑:
1. 输出0:p的地址
p是指向结构体变量x的指针,这里输出的是x在栈内存中的起始地址,不同运行环境下这个值会不一样,属于正常现象。
2. 输出1:sizeof(*p)=18的原因
*p等价于struct X类型,它的大小由成员大小和内存布局决定:
- 64位系统下,指针类型占8字节,所以
struct X *p和short *b各占8字节 short a是短整型,占2字节
三个成员的总大小是8+2+8=18,这里没有额外的内存填充,说明当前编译环境没有强制要求结构体总大小对齐到最大成员(8字节)的倍数(C标准允许这种情况,只是部分架构下不对齐的指针访问会有性能损耗)。
3. 输出2:&p->a的地址
结构体成员在内存中按声明顺序排列,第一个成员p占8字节,所以第二个成员a的起始地址是x的起始地址加8,也就是0x7ffeefbff5e0 + 8 = 0x7ffeefbff5e8,和输出完全匹配。
4. 输出3:&p->b的地址
a是2字节的短整型,所以第三个成员b的起始地址是a的地址加2,也就是0x7ffeefbff5e8 + 2 = 0x7ffeefbff5ea,和输出一致。
5. 输出4:++p的结果
指针自增的步长是指向类型的大小,也就是sizeof(struct X)=18(十六进制为0x12)。p原本指向0x7ffeefbff5e0,自增后地址变为0x7ffeefbff5e0 + 0x12 = 0x7ffeefbff5f2,和输出一致。
6. 输出5:p-&x的结果
C语言中同类型指针相减,结果是两个指针之间相差的元素个数,而非字节数。p现在指向x之后的下一个struct X位置,所以差值是1,输出1。
7. 输出6:&x-p的结果
这是&x - p,计算得0 - 1 = -1。因为printf用了%x格式符,会把数值当作无符号十六进制输出,-1的32位无符号表示是ffffffff(这里输出32位是因为%x默认处理32位整数,64位指针被截断了)。
内容的提问来源于stack exchange,提问作者KeyboardSound

