什么是字节对齐,为什么要对齐?
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。
一、首先在没有声明#pragma pack(n)来设定变量以n字节对齐方式的情况下:
偏移量的求法:VC对一些变量的起始地址做了“对齐”处理。在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式(32位系统)。
类型
对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
Char
偏移量必须为sizeof(char)即1的倍数
int
偏移量必须为sizeof(int)即4的倍数
float
偏移量必须为sizeof(float)即4的倍数
double
偏移量必须为sizeof(double)即8的倍数
Short
偏移量必须为sizeof(short)即2的倍数
VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。 即struct的所占空间为该结构中占用最大空间的类型所占用的字节数的倍数;
例如:
//main.cpp程序入口函数(主函数)
#include<ctime>
#include<conio.h>
#include"time.h"
#include<iostream>
#include<cstdio>
#include<cstdio>
using namespace std;
#define Trace(type,field) ((size_t)&(((type*)0)->field))
//#pragma pack(4)
struct Mystr
{
char a;
int b;
float c;
double d;
};
void main()
{
/*char* a = "adf";
char* b = "ahdf";
cout << strCmp(a, b);*/
cout << sizeof(Mystr); //结果是24
printf("%d", Trace(Mystr,d)); //结果是16 因为sizeof(double)为8,所以偏移量要为8的倍数 把前面的float的后四个字节补零
}
二、
VC 中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:
第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。
并且此时的struct所占空间总大小也有个约束条件,分下面两种情况:
1、如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;
2、否则必须为n的倍数!
个人感觉这里的n相当于设定了偏移基数的最大值,也设定了struct的最大空间倍数的基数。
eg:n小于double的情况,按n的倍数计算struct的空间大小,和按n的倍数计算偏移量
//main.cpp程序入口函数(主函数)
#include<ctime>
#include<conio.h>
#include"time.h"
#include<iostream>
#include<cstdio>
#include<cstdio>
using namespace std;
#define Trace(type,field) ((size_t)&(((type*)0)->field))
#pragma pack(4)
struct Mystr
{
char a;
int b;
float c;
double d;
};
void main()
{
/*char* a = "adf";
char* b = "ahdf";
cout << strCmp(a, b);*/
cout << sizeof(Mystr); //结果是20
printf("%d", Trace(Mystr,d)); //结果是12
}
eg:n大于double的情况,按double的倍数计算struct的空间大小,和按double的倍数计算偏移量;
//main.cpp程序入口函数(主函数)
#include<ctime>
#include<conio.h>
#include"time.h"
#include<iostream>
#include<cstdio>
#include<cstdio>
using namespace std;
#define Trace(type,field) ((size_t)&(((type*)0)->field))
#pragma pack(16)
struct Mystr
{
char a;
int b;
float c;
double d;
};
void main()
{
/*char* a = "adf";
char* b = "ahdf";
cout << strCmp(a, b);*/
cout << sizeof(Mystr); //结果是24
printf("%d", Trace(Mystr,d)); //结果是16
}