为了让读者加深印象,我们这里在代码中没有使用0,而是使用的4,所以在最终计算出的结果部分减去了一个4才是偏移地址,当然实际使用中我们都是用的是0。
懂了上面的宏offsetof之后我们再来看看下面的代码:
- #include <stdio.h>
-
- #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-
- typedef struct stu1
- {
- int a;
- char b[1];
- int c;
- }stu1;
-
- void main()
- {
- printf("offsetof(stu1,a):\t%d\n",offsetof(stu1,a));
- printf("offsetof(stu1,b):\t%d\n",offsetof(stu1,b));
- printf("offsetof(stu1,c):\t%d\n",offsetof(stu1,c));
- printf("sizeof(stu1) :\t%d\n",sizeof(stu1));
- }
|
运行结果为:
- offsetof(stu1,a): 0
- offsetof(stu1,b): 4
- offsetof(stu1,c): 8
- sizeof(stu1) : 12
- Press any key to continue
|
对于字节对齐不了解的读者可能有疑惑的是c的偏移量怎么会是8和结构体的大小怎么会是12呢?因该是sizeof(int)+sizeof(char)+sizeof(int)=9。其实这是编译器对变量存储的一个特殊处理。为了提高CPU的存储速度,编译器对一些变量的起始地址做了对齐处理。在默认情况下,编译器规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。现在来分析下上面的代码,如果我们假定a的起始地址为0,它占用了4个字节,那么接下来的空闲地址就是4,是1的倍数,满足要求,所以b存放的起始地址是4,占用一个字节。接下来的空闲地址为5,而c是int变量,占用4个字节,5不是4的整数倍,所以向后移动,找到离5最近的8作为存放c的起始地址,c也占用4字节,所以最后使得结构体的大小为12。现在我们再来看看下面的代码:
- #include <stdio.h>
-
- typedef struct stu1
- {
- char array[7];
- }stu1;
-
- typedef struct stu2
- {
- double fa;
- }stu2;
-
- typedef struct stu3
- {
- stu1 s;
- char str;
- }stu3;
-
- typedef struct stu4
- {
- stu2 s;
- char str;
- }stu4;
-
- void main()
- {
- printf("sizeof(stu1) :\t%d\n",sizeof(stu1));
- printf("sizeof(stu2) :\t%d\n",sizeof(stu2));
- printf("sizeof(stu3) :\t%d\n",sizeof(stu3));
- printf("sizeof(stu4) :\t%d\n",sizeof(stu4));
- }
|