内存中的数据对齐

内存中的数据对齐

先看个例子:

#include <stdio.h>

struct {
	short sA;
	short sB;
	short sC;
}A;
struct {
	long lA;
	short sB;
}B;
struct {
	char chA;
	int iB;
	char chC;
}C;
struct {
	char chA;
	char chB;
	int iC;
}D;
struct {
	int iA;
	char chB;
	char chC;
}E;

int main()
{
	int iA;
	char chB;
	int iC;

	printf("sizeof(short): %d\n", sizeof(short));
	printf("sizeof(long): %d\n", sizeof(long));
	printf("sizeof(char): %d\n", sizeof(char));
	printf("sizeof(int): %d\n\n", sizeof(int));

	printf("sizeof(A): %d\n", sizeof(A));
	printf("&A: 0x%08x\n", &A);
	printf("&(A.sA): 0x%08x\n", &(A.sA));
	printf("&(A.sB): 0x%08x\n", &(A.sB));
	printf("&(A.sC): 0x%08x\n\n", &(A.sC));

	printf("sizeof(B): %d\n", sizeof(B));
	printf("&B: 0x%08x\n", &B);
	printf("&(B.lA): 0x%08x\n", &(B.lA));
	printf("&(B.sB): 0x%08x\n\n", &(B.sB));

	printf("sizeof(C): %d\n", sizeof(C));
	printf("&C: 0x%08x\n", &C);
	printf("&(C.chA): 0x%08x\n", &(C.chA));
	printf("&(C.iB): 0x%08x\n", &(C.iB));
	printf("&(C.chC): 0x%08x\n\n", &(C.chC));

	printf("sizeof(D): %d\n", sizeof(D));
	printf("&D: 0x%08x\n", &D);
	printf("&(D.chA): 0x%08x\n", &(D.chA));
	printf("&(D.chB): 0x%08x\n", &(D.chB));
	printf("&(D.iC): 0x%08x\n\n", &(D.iC));

	printf("sizeof(E): %d\n", sizeof(E));
	printf("&E: 0x%08x\n", &E);
	printf("&(E.iA): 0x%08x\n", &(E.iA));
	printf("&(E.chB): 0x%08x\n", &(E.chB));
	printf("&(E.chC): 0x%08x\n\n", &(E.chC));

	printf("&iA: 0x%08x\n", &iA);
	printf("&chB: 0x%08x\n", &chB);
	printf("&iC: 0x%08x\n", &iC);

	return 0;
}


输出结果为:


第一幅为VS2005在默认设置情况下输出结果,第二幅为gcc3.4.4在默认情况下的输出结果,第三幅为VS2005在设置/Zp1后的输出结果。

《程序员面试宝典》摘录:“默认情况下,为了方便对结构体内的元素的访问和管理,当结构体内元素的长度都小于处理器的位数的时候,便以结构体里面最长的数据元素为对齐单位,也就是说,结构体的长度一定是最长的数据元素的整数倍。如果结构体内存在长度大于处理器位数的元素,那么就以处理器的位数为对齐单位。但是结构体类型相同的连续元素将在连续的空间内,和数组一样。”,“CPU的优化规则大致原则是这样的:对于n字节的元素(n=2,4,8,...),它的首地址能被n整除,才能获得最好的性能。设计编译器的时候可以遵循这个原则:对于每一个变量,可以从当前位置向后找到第一个满足这个条件的地址作为首地址。对于B来说,即便采用这个要求,得到的结果也应该是6字节。但是结构体一般会面临数组分配的问题。编译器为了优化这种情况,干脆把它的大小设为8字节,这样就没有麻烦了,否则的话,会出现单个结构体的大小为6字节,而大小为n的结构体数组大小却为8×(n-1)+6的尴尬局面。”,“数据对齐,是指数据所在的内存地址必须是该数据长度的整数倍。DWORD数据的内存起始地址能被4整除,WORD数据的内存起始地址能被2除尽。x86CPU能直接访问对齐的数据,当它试图访问一个未对齐的数据时,会在内部进行一系列的调整。这些调整对于程序来说是透明的,但是会降低运行速度,所以编译器在编译程序是会尽量保证数据对齐。”

上面的输出结果是两种编译器默认情况下的输出。内存对齐是由编译器控制的,设计编译器时已经考虑到如何解决内存对齐问题了。特别是在VS2005中有相应的编译器选项来进行对内存对齐进行一定的设置。该编译器选项为“/Zp[1/2/4/8/16]”,如/Zp1。

《MSDN》摘录:“当指定此选项时,第一个结构成员后的每个结构成员将存储在成员类型大小或 n 字节边界(其中 n 为 1、2、4、8 或 16)两者中较小的一个边界上。

下表描述了可用的值。
1
在 1 字节边界上封装结构。与 /Zp 相同。
2
在 2 字节边界上封装结构。
4
在 4 字节边界上封装结构。
8
在 8 字节边界上封装结构(默认操作)。
16
在 16 字节边界上封装结构。
除非有特定的对齐要求,否则不应使用此选项。”

输出结果是受编译器的控制,一般是按编译器默认情况下,结构体中的元素的长度都小于处理器的位数或者小于编译器默认的对齐长度。这样其实就是“按结构体中最长元素的长度为对齐单位,每个结构体成员的内存地址需要是这个对齐长度的整数倍,例外是类型相同且连续的成员也连续;结构体的长度也一定是最长的数据元素长度的整数倍,另外结构体本身的内存地址也需要是结构体大小的整数倍”,主要抓住这几点就差不多了。

分析:

1、结构体变量A,最长元素长度为2,每个成员连续存放刚好满足每个成员存储在2的边界上,而且A的内存大小为6刚好为2的整数倍。

2、结构体变量B,最长元素长度为4,成员存放仍然连续,满足每个成员在4的边界上,B的内存大小需要是8的时候满足是4的整数倍,6不符合这个要求。而且B的内存位置也需要在8的整数倍上,所以可以看到输出结果中结构体A完了之后有两个字节的空间没有使用,然后才是结构体B,(VS2005的输出结果,GCC中可能编译器做了些其他优化操作,它的内存分布是先B然后A),B内存空间最后又有两个字节是填补的。

3、结构体变量C,最长元素长度为4,第一个元素的起始必须是4的整数倍,由于第二个元素和第一个元素类型不同则起始地址也需要是4的整数倍,所以第一个元素之后有三个字节空间的浪费,第三个连续第二个存放满足起始地址是4的整数倍,现在是9个字节的大小,由于要求结构体大小是4的整数倍,所以结尾添加3个字节的填补空间,最后结构体的大小为12。

4、结构体变量D,最长元素长度为4,第一个和第二个元素类型同,二者连续存放,且第一个元素的起始位置是4的整数倍,由于要求第三个元素的位置也是4的整数倍,所以第二个元素之后填补了两个字节的空间。结构体总大小为8。

5、结构体变量E的情况可参考B和D。

不过从输出的结果上看,结构体的起始地址都是按4字节对齐的。


另外,对于main函数中的三个局部变量,从GCC的输出可以看到,栈的增长方向为从内存高地址到低地址,也即所谓下推栈,按变量定义的次序进行压栈,而且第三个变量的起始地址为了在4的整数倍上,在第二个变量只有填补了三个字节的空间。VS2005的输出每个局部变量之间都有8个字节的未用空间,不太懂。


最后在补充:

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str[] = {"he", "the"};
	cout << sizeof(string) << endl;
	cout << sizeof(str) << endl;

	return 0;
}

在不同的编译器下,输出结果不同。VS2005的输出为64,32。gcc的输出为8,4。无论你的string里放多长的字符串,它的sizeof()都是固定的,字符串所占的空间是从堆中动态分配的,与sizeof()无关。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值