结构体成员内存分布与对齐

先来看一段代码,这是曾经的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默认),各个成员在内存中的分布情况如下(横线表示该内存单元被占据):

————————————————
————  ————————
————————————————
name数组肯定要分配在一个连续的内存空间中,但是首地址必须是8的倍数,所以这里首地址是2686728,当他分配完毕时,下面该分配age的内存了,这个时候name尾地址是2686737,下一个是2686738,他不能被N=min(4,8)整除,直到2686740才整除,所以这个是age的首地址,后面的score同理,故如上如图显示的那样,本程序中的st结构体共占用24个字节空间,两个被浪费。

如果我们改变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#






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值