先来看一段代码,这是曾经的IBM和微软的笔试题:
#include <iostream>
struct student
{
char name[10];
int age;
double score;
};
int main (void)
{
using namespace std;
struct student st;
cout << sizeof(st) << endl;
cout << (unsigned int)&st.name << endl;
cout << (unsigned int)&st.age << endl;
cout << (unsigned int)&st.score << endl;
}
输出的结果是:
为什么结果会是这样的呢?按理来说,st结构体的大小应该是10+4+8=22才对,怎么会是24呢?
其实原因在于,结构体在存放到内存中时是要进行内存对齐的,那么究竟是怎么对齐的呢?下面来探讨一下:
要弄清楚上面问题是怎么回事,就要搞清楚结构体在内存中是怎么存放的。其实这些问题即与硬件相关,又与软件相关。所谓的与软件相关是说和具体的语言和编译器有关,编译器为了优化CPU访问内存的效率,在生成结构体成员的起始地址时遵循某种特定的规则,这就是结构体成员对齐;所谓硬件相关是说和CPU的字节序有关,也就是大于一个字节型的数据如int,long等在内存中存放的顺序问题,即单个字节中bit位和地址的高低对应的关系。字节序分为两类:Big-Endian和Little-Endian,通常称为大端法和小端法,他们的定义是:
大端法:将一个字节中的每个bit位中的低位放在地址中的高位处,而每个bit位中高位放在地址中的低位
小端法:将一个字节中的每个bit位中的低位放在地址中的低位处,而每个bit位中高位放在地址中的高位
Intel,VAX,Unisys处理器的计算机都是小端法,而IBM大型机和UNIX平台的机器都是大端法的。这里我们仅讨论Intel小端法的机器。
前面说了,为了优化CPU对内的访问效率,编译器做了内存分配的处理,处理的规则大致如下:
对与n字节的元素,他的首地址能被n整除,这种规则称为“对齐”。对于结构体来说结构体成员在内存中按顺序存放,所占字节地址依次升高,第一个成员处于低地址,最后一个成员处于最高的地址,但是成员的存储不一定是连续的,编译器会对每个成员做前面介绍的“对齐”当时处理。
通常编译器中可以设置一个对齐参数n,但是这个n并不是成员的实际对齐参数,如在大多数的IDE中(如VC 6.0)中的实际对齐参数N是这样计算得到的:N=min(sizeof(成员类型),n),也就是说N取成员类型的字节数和编译器的对其参数中的最小值,结构体所有成员的N最大值称为结构体的对其参数。
接下来,我们详细讨论结构体成员的分步情况。顺便做个假设,常见类型的大小:int 4字节,short 2字节,char 1字节,double 8字节,long 4字节
还分析开头的那个例子:
#include <iostream>
struct student
{
char name[10];
int age;
double score;
};
int main (void)
{
using namespace std;
struct student st;
cout << sizeof(st) << endl;
cout << (unsigned int)&st.name << endl;
cout << (unsigned int)&st.age << endl;
cout << (unsigned int)&st.score << endl;
}
在这里n值是8(一般IDE默认),各个成员在内存中的分布情况如下(横线表示该内存单元被占据):
—— | —— | —— | —— | —— | —— | —— | —— |
—— | —— | —— | —— | —— | —— | ||
—— | —— | —— | —— | —— | —— | —— | —— |
如果我们改变IDE中的n值呢?可以用#pragma pack(n) //n=1,2,4,8,16来改变n值,其结果的分析和上面一样
实例代码:
/*************************************************************************
> File Name: nei_cun_dui_qi.c
> Author: Baniel Gao
> Mail: createchance@163.com
> Blog: blog.youkuaiyun.com/createchance
> Created Time: Mon 16 Dec 2013 19:19:03 CST
************************************************************************/
#include <stdio.h>
#pragma pack(2)
typedef struct
{
char name[10];
int num;
double price;
} BOOK;
int main(void)
{
BOOK book;
printf("&book.name = %p \n",book.name);
printf("&book.num = %p \n",&book.num);
printf("&book.price = %p \n",&book.price);
printf("sizeof(BOOK) = %ld \n",sizeof(BOOK));
return 0;
}
运行结果:
root@ubuntu:/home/linux/Code/Section1# ./a.out
&book.name = 0x7fffa6a5f460
&book.num = 0x7fffa6a5f46a
&book.price = 0x7fffa6a5f46e
sizeof(BOOK) = 22
root@ubuntu:/home/linux/Code/Section1#