字节对齐
1. 基本概念
字节对齐:计算机存储系统中以Byte为单位存储数据,不同数据类型所占的空间不同,如:整型(int)数据占4个字节,字符型(char)数据占一个字节,短整型(short)数据占两个字节,等等。计算机为了快速的读写数据,默认情况下将数据存放在某个地址的起始位置,如:整型数据(int)默认存储在地址能被4整除的起始位置,字符型数据(char)可以存放在任何地址位置(被1整除),短整型(short)数据存储在地址能被2整除的起始位置。这就是默认字节对齐方式。
2. 举例说明
很显然默认对齐方式会浪费很多空间,例如如下结构:
struct student
{
char name[5];
int num;
short score;
}
本来只用了11bytes(5+4+2)的空间,但是由于int型默认4字节对齐,存放在地址能被4整除的起始位置,即:如果name[5]从0开始存放,它占5bytes,而num则从第8(偏移量)个字节开始存放。所以sizeof(student)=16。于是中间空出几个字节闲置着。但这样便于计算机快速读写数据,是一种以空间换取时间的方式。其数据对齐如下图:
|char|char|char|char|
|char|----|----|----|
|--------int--------|
|--short--|----|----|
如果我们将结构体中变量的顺序改变为:
struct student
{
int num;
char name[5];
short score;
}
则,num从0开始存放,而name从第4(偏移量)个字节开始存放,连续5个字节,score从第10(偏移量)开始存放,故sizeof(student)=12。其数据对齐如下图:
|--------int--------|
|char|char|char|char|
|char|----|--short--|
如果我们将结构体中变量的顺序再次改为为:
struct student
{
int num;
short score;
char name[5];
}
则,sizeof(student)=12。其数据对齐如下图:
|--------int--------|
|--short--|char|char|
|char|char|char|----|
验证代码如下:
- #include <stdio.h>
- typedef struct
- {
- char name[5];
- int num;
- short score;
- }student1;
- typedef struct
- {
- int num;
- char name[5];
- short score;
- }student2;
- typedef struct
- {
- int num;
- short score;
- char name[5];
- }student3;
- int main()
- {
- student1 s1={"Tom",1001,90};
- student2 s2={1002,"Mike",91};
- student3 s3={1003,92,"Jack"};
- printf("student1 size = %d/n",sizeof(s1));
- printf("student2 size = %d/n",sizeof(s2));
- printf("student3 size = %d/n",sizeof(s3));
- printf("/nstudent1 address : 0x%08x/n",&s1);
- printf(" name address : 0x%08x/n",s1.name);
- printf(" num address : 0x%08x/n",&s1.num);
- printf(" score address : 0x%08x/n",&s1.score);
- printf("/nstudent2 address : 0x%08x/n",&s2);
- printf(" num address : 0x%08x/n",&s2.num);
- printf(" name address : 0x%08x/n",s2.name);
- printf(" score address : 0x%08x/n",&s2.score);
- printf("/nstudent3 address : 0x%08x/n",&s3);
- printf(" num address : 0x%08x/n",&s3.num);
- printf(" score address : 0x%08x/n",&s3.score);
- printf(" name address : 0x%08x/n",s3.name);
- return 0;
- }
运行结果如下:
- student1 size = 16
- student2 size = 12
- student3 size = 12
- student1 address : 0x0013ff70
- name address : 0x0013ff70
- num address : 0x0013ff78
- score address : 0x0013ff7c
- student2 address : 0x0013ff64
- num address : 0x0013ff64
- name address : 0x0013ff68
- score address : 0x0013ff6e
- student3 address : 0x0013ff58
- num address : 0x0013ff58
- score address : 0x0013ff5c
- name address : 0x0013ff5e
3. #pragma pack()命令
为了节省空间,我们可以在编码时通过#pragma pack()命令指定程序的对齐方式,括号中是对齐的字节数,若该命令括号中的内容为空,则为默认对齐方式。例如,对于上面第一个结构体,如果通过该命令手动设置对齐字节数如下:
#pragma pack(2) //设置2字节对齐
struct strdent
{
char name[5]; //本身1字节对齐,比2字节对齐小,按1字节对齐
int num; //本身4字节对齐,比2字节对齐大,按2字节对齐
short score; //本身也2字节对齐,仍然按2字节对齐
}
#pragma pack() //取消设置的字节对齐方式
则,num从第6(偏移量)个字节开始存放,score从第10(偏移量)个字节开始存放,故sizeof(student)=12,其数据对齐如下图:
|char|char|
|char|char|
|char|-----|
|----int----|
|----int----|
|--short---|
这样改变默认的字节对齐方式可以更充分地利用存储空间,但是这会降低计算机读写数据的速度,是一种以时间换取空间的方式。
验证代码如下:
- #include <stdio.h>
- #pragma pack(2)
- typedef struct
- {
- char name[5];
- int num;
- short score;
- }student1;
- typedef struct
- {
- int num;
- char name[5];
- short score;
- }student2;
- typedef struct
- {
- int num;
- short score;
- char name[5];
- }student3;
- #pragma pack()
- int main()
- {
- student1 s1={"Tom",1001,90};
- student2 s2={1002,"Mike",91};
- student3 s3={1003,92,"Jack"};
- printf("student1 size = %d/n",sizeof(s1));
- printf("student2 size = %d/n",sizeof(s2));
- printf("student3 size = %d/n",sizeof(s3));
- printf("/nstudent1 address : 0x%08x/n",&s1);
- printf(" name address : 0x%08x/n",s1.name);
- printf(" num address : 0x%08x/n",&s1.num);
- printf(" score address : 0x%08x/n",&s1.score);
- printf("/nstudent2 address : 0x%08x/n",&s2);
- printf(" num address : 0x%08x/n",&s2.num);
- printf(" name address : 0x%08x/n",s2.name);
- printf(" score address : 0x%08x/n",&s2.score);
- printf("/nstudent3 address : 0x%08x/n",&s3);
- printf(" num address : 0x%08x/n",&s3.num);
- printf(" score address : 0x%08x/n",&s3.score);
- printf(" name address : 0x%08x/n",s3.name);
- return 0;
- }
运行结果如下:
- student1 size = 12
- student2 size = 12
- student3 size = 12
- student1 address : 0x0013ff74
- name address : 0x0013ff74
- num address : 0x0013ff7a
- score address : 0x0013ff7e
- student2 address : 0x0013ff68
- num address : 0x0013ff68
- name address : 0x0013ff6c
- score address : 0x0013ff72
- student3 address : 0x0013ff5c
- num address : 0x0013ff5c
- score address : 0x0013ff60
- name address : 0x0013ff62
若该为#pragma pack(1),则运行结果如下:
- student1 size = 11
- student2 size = 11
- student3 size = 11
- student1 address : 0x0013ff74
- name address : 0x0013ff74
- num address : 0x0013ff79
- score address : 0x0013ff7d
- student2 address : 0x0013ff68
- num address : 0x0013ff68
- name address : 0x0013ff6c
- score address : 0x0013ff71
- student3 address : 0x0013ff5c
- num address : 0x0013ff5c
- score address : 0x0013ff60
- name address : 0x0013ff62
4. 例子
程序如下:
- #include <stdio.h>
- class A1
- {
- public:
- int a;
- static int b;
- A1();
- ~A1();
- };
- class A2
- {
- public:
- int a;
- char c;
- A2();
- ~A2();
- };
- class A3
- {
- public:
- float a;
- char c;
- A3();
- ~A3();
- };
- class A4
- {
- public:
- float a;
- int b;
- char c;
- A4();
- ~A4();
- };
- class A5
- {
- public:
- double d;
- float a;
- int b;
- char c;
- A5();
- ~A5();
- };
- main()
- {
- printf("A1 size = %d/n",sizeof(A1));
- printf("A2 size = %d/n",sizeof(A2));
- printf("A3 size = %d/n",sizeof(A3));
- printf("A4 size = %d/n",sizeof(A4));
- printf("A5 size = %d/n",sizeof(A5));
- }
该例子采取默认对齐方式,运行结果如下:
- A1 size = 4
- A2 size = 8
- A3 size = 8
- A4 size = 12
- A5 size = 24
A5结构体大小按照字节对齐为8+4+4+4=20,还必须满足能被结构体中每个变量所占的大小整除。因为有double,所以为24字节。
说明:静态变量存放在全局数据区内,而sizeof计算栈中分配的空间的大小,故不计算在内。
若加上#pragma pack(2)命令,则运行结果如下:
- A1 size = 4
- A2 size = 6
- A3 size = 6
- A4 size = 10