标签:
在定义结构体类型后,则结构体中的成员内存布局就定下了。
#include <stdio.h>
typedef struct Test
{
int a;
int b;
int c;
}Test;
int main()
{
Test t;
Test*p = NULL;
p = &t;
//b相对于结构体Test的偏移量,n1=4
int n1 = (int)&(p->b) - (int)p;
//绝对0地址b的偏移量, n2=4
int n2 = (int)&( ( (Test *)0 )->b);
printf("n1:%d \n", n1);
printf("n2:%d \n", n2);
return 0;
}
在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题。从理论上讲,对于任何变量的访问都可以从任何地址开始访问,但是事实上不是如此,实际上访问特定类型的变量只能在特定的地址访问,这就需要各个变量在空间上按一定的规则排列, 而不是简单地顺序排列,这就是内存对齐。
测试代码:
#include <stdio.h>
typedef struct Test
{
char a[5]; //5字节
int b; //4字节
int c; //4字节
}Test;
int main()
{
Test t;
Test*p = NULL;
p = &t;
//b相对于结构体Test的偏移量, n1的值为8,不是5
int n1 = (int)&(p->b) - (int)p;
//绝对0地址b的偏移量
int n2 = (int)&( ( (Test *)0 )->b);
printf("n1:%d \n", n1);
printf("n2:%d \n", n2);
//sizeof(Test) = 8+4+4 = 16, 不是5+4+4
printf("sizeof(Test) = %d\n", sizeof(Test));
return 0;
}
原则1:数据成员的对齐规则(默认以最大的类型字节为单位)
结构体(struct)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存放在offset为该数据成员类型大小的整数倍的地方(比如int在32位机器为4字节,则要从4的整数倍地址开始存储)
原则2:结构体作为成员的对齐规则
如果一个结构体B里嵌套另一个结构体A,还是以最大成员类型的字节对齐,但是结构体A存储起点为A内部最大成员整数倍的地方。(struct B里存有struct A,A里有char,int,double等成员,那A应该从8的整数倍开始存储。),结构体A中的成员的对齐规则仍满足原则1、原则2。
注意:
1)结构体A所占的大小为该结构体成员内部最大元素的整数倍,不足补齐。
2)不是直接将结构体A的成员直接移动到结构体B中。
原则3:收尾工作
结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
默认情况下,以结构体最大成员的类型字节大小对齐,当然也可以通过程序控制,指定字节对齐的单位:
#pragma pack(xx) //xx必须是2的n次方
#pragma pack(1) //1字节对齐
#pragma pack(2) //2字节对齐
#pragma pack(4) //4字节对齐
#pragma pack(8) //8字节对齐
#pragma pack(16) //16字节对齐
注意:尽管可以通过pragma pack()指定字节对齐的单位,但是,只有当结构体成员大于或等于pragma pack()指定字节对齐的单位,指定字节对齐的单位才有效,否则,还是以结构体最大成员的类型字节大小对齐
结构体(struct)的数据成员,第一个数据成员放在偏移量为0的地方,以后每个数据成员存放在偏移量为该数据成员类型大小的整数倍的地方,但是,如果该数据成员类型大小大于字节对齐大小,那么偏移量为字节对齐大小的整数倍。
struct
{
char a;
int b;
short c;
}A;
以1字节对齐
当以1字节对齐时,结构体的大小则为各成员空间大小相加:
sizeof(A) = 1 + 4 + 2 = 7
以2字节对齐
a: 1 * 0 = 0
b: 2 * 1 = 2 (int为4字节, 大于对齐字节(2字节),以2字节计算)
c: 2 * 3 = 6
综上:
a放在偏移量为0的地方,由于a为char类型,大小为1字节,所以,占1个格子。
b放在偏移量为2的地方,由于b为int类型,大小为4字节,所以,占4个格子,放2行,而上面的a,只占一个字节,不足对齐字节(2字节),故不足不齐(以”*”描述)。
c放在偏移量为6的地方,由于c为short类型,大小为2字节,所以,占2个格子。
所以,sizeof(A) = 8
以4字节对齐
a: 1 * 0 = 0
b: 4 * 1 = 4
c: 2 * 4 = 8
sizeof(A) = 12
以8或16字节对齐
由于结构体成员最大的类型字节为4,小于8或16,所以,尽管通过pragma pack(xx)指定对齐的字节,但,还是以结构体成员最大的类型字节对齐,还是以4字节对齐。
struct
{
char a[5];
double b;
short c;
}A;
以2字节对齐
a: 1 * 0 = 0
b: 2 * 3 = 6 (double为8字节, 大于对齐字节(2字节),以2字节计算)
c: 2 * 7 = 14
sizeof(A) = 16
以4字节对齐
a: 1 * 0 = 0
b: 4 * 2 = 8 (double为8字节, 大于对齐字节(4字节),以4字节计算)
c: 2 * 8 = 16
sizeof(A) = 20
以8或16字节对齐
a: 1 * 0 = 0
b: 8 * 1 = 8
c: 2 * 8 = 16
sizeof(A) = 24
struct A
{
char e;
short f;
};
struct
{
int a;
char b;
struct A c;
char d;
}B;
结构体B里嵌套另一个结构体A,则以两个结构体中最大的成员对齐,B中最大成员为int,4字节,则以4字节对齐。但是,A中的最大成员为short,2个字节,A存储起点位置必须为2的整数倍。
a: 4 * 0 = 0
b: 1 * 4 = 4
结构体起点位置:2*3=6, 为结构体A最大成员的整数倍
?e: 6 + 1*0 = 6
?f: 6 + 2*1 = 8
d: 1 * 10 = 10
sizeof(B) = 12
struct A
{
char e;
short f;
int g;
};
typedef struct B
{
int a;
char b;
struct A c;
char d;
}B;
结构体B里嵌套另一个结构体A,则以两个结构体中最大的成员对齐,B中最大成员为int,4字节,则以4字节对齐。但是,A中的最大成员为int,4个字节,A存储起点位置必须为4的整数倍。
a: 4 * 0 = 0
b: 1 * 4 = 4
结构体起点位置:2*4=8, 为结构体A最大成员的整数倍
?e: 8 + 1*0 = 8
?f: 8 + 2*1 = 10
?g: 8 + 4*1 = 12
d: 1 * 16 = 16
sizeof(B)=20
struct A
{
char e;
short f;
double g;
};
typedef struct B
{
int a;
char b;
struct A c;
char d;
}B;
结构体B里嵌套另一个结构体A,则以两个结构体中最大的成员对齐,最大成员为double,8字节,则以8字节对齐。但是,A中的最大成员为double,8个字节,A存储起点位置必须为8的整数倍。
a: 4 * 0 = 0
b: 1 * 4 = 4
结构体起点位置:8*1=8, 为结构体A最大成员的整数倍
?e: 8 + 1*0 = 8
?f: 8 + 2*1 = 10
?g: 8 + 8*1 = 16
d: 1 * 24 = 24
sizeof(B) = 32
使用位域的主要目的是压缩存储,其大致规则为:
struct A
{
int a1 : 5;
int a2 : 9;
char b;
int c : 4;
short d;
}B;
由于a1和a2是紧挨着,而且类型相同,故a1和a2压缩放在一起,a1+a2=14位 < 32位,小于int类型的大小,故a1和a2只需要1个int类型大小的空间,尽管没有只占14位,后面的不足补齐。
c尽管只占4位,但由于前后的成员(b, d)类型都不是int,故c需要4字节(32位)空间存放。
以1字节对齐
a1+a2 = 14位,小于4字节(int类型为4字节),所以,a1和a2放在一起。
sizeof(B) = 4 + 1 + 4 + 2 = 11
以2字节对齐
a1+a2: 2 * 0 = 0 (int为4字节, 大于对齐字节(2字节),以2字节计算)
b: 1 * 4 = 4
c: 2 * 3 = 6 (int为4字节, 大于对齐字节(2字节),以2字节计算)
d: 2 * 5 = 10
sizeof(B) = 12
以4、8或16字节对齐
a1+a2: 4 * 0 = 0
b: 4 * 1 = 4
c: 4 * 2 = 8
d: 2 * 6 = 12
sizeof(B) = 16
参考资料:结构体字节对齐
标签:
原文地址:http://blog.csdn.net/tennysonsky/article/details/51355690