C/C++之内存对齐
数据对齐,是指数据所在的内存地址必须是该数据长度的整数倍。DWORD数据的内存起始地址能被4除尽,WORD数据的内存起始地址能被2除尽。X86 CPU能直接访问对齐的数据,当它试图访问一个未对齐的数据时,会在内部进行一系列的调整。这些调整对于程序员来说是透明的,但是会降低运行速度,所以编译器在编译程序时会尽量保证数据对齐。
不同的编译器内存对齐的方式不同。
一个小例子:在32位的机器上,数据是以4字节为对齐单位,这两个类的输出结果为什么不同?(VS2008)
- #include <iostream>
- using namespace std;
- class B
- {
- private:
- bool m_bTemp;
- int m_nTemp;
- bool m_bTemp2;
- };
- class C
- {
- private:
- int m_nTemp;
- bool m_bTemp;
- bool m_bTemp2;
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- cout<<sizeof(B)<<endl;
- cout<<sizeof(C)<<endl;
- system("pause");
- return 0;
- }
答案是:3*4=12,2*4=8
分析:在访问内存时,如果地址按4字节对齐,则访问的效率会高很多。
考虑到性能方面,编译器会对结构进行对齐处理,考虑下面的结构:
struct aStruct
{
char cValue;
int iValue;
};
直观地讲,这个结构的尺寸是sizeof(char)+sizeof(int)=6,但是在实际编译下, 这个结构尺寸默认是8,因为第二个域iValue会被对齐到第4个字节。
注意:字节对齐是编译时决定的,一旦决定则不会再改变,因此即使有对齐的因素存在,也不会出现一个结构在运行时尺寸发生变化的情况。
在本题中:第一种类的数据对齐是下面的情况:
bool ---- ---- ----
------- int ---------
bool ----- ---- ----
第二种类的数据对齐是下面的情况:
------- int ----------
bool bool ---------
所以类的大小分别3*4和2*4
一般在VC++中加上#pragma pack(n)设置内存对齐。
我们可以利用#pragma pack()来改变编译器的默认对齐方式。
#pragma pack(n) //编译器将按照n字节对齐
#pragma pack() //编译器将取消自定义字节对齐方式
在#pragma pack(n)和#pragma pack()之间的代码按n字节对齐。
但是成员对齐有一个重要的条件,即每个成员按照自己的对齐方式对齐;也就是说虽然指定了按n字节对齐,但并不是所有的成员都以n字节对齐。
对齐的规则是:每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是n字节)中较小的一个对齐,即min(n,sizeof(item)),并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节。
- #pragma pack(8)
- struct TestStruct4
- {
- char a;
- long b;
- };
- struct TestStruct5
- {
- char c;
- TestStruct4 d;
- long long e;
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- cout<<sizeof(TestStruct4)<<endl;
- cout<<sizeof(TestStruct5)<<endl;
- system("pause");
- return 0;
- }
运行结果为:8,24
分析:
TestStruct4 中,成员a是1字节,默认按照1字节对齐,指定对齐参数是8,这两个值中取1,a按1字节对齐;
成员b是4字节,默认是按4字节对齐,这时就按4字节对齐,所以sizeof(TestStruct4)应该是8.
TestStruct5 中,c和TestStruct4中的a一样,按1字节对齐;而d是个结构,它是8字节,对于结构来说,它的默认对齐方式就是其所有成员使用的对齐参数中最大的一个,TestStruct4就是4,所以成员d就按照4字节对齐。成员e是8字节,它是默认的8字节对齐,和指定的一样,所以它对齐到8自己的边界上,这时,已经使用了12字节了,所以又添加了4字节的空间,从第16字节开始放置成员e。这时长度为24,已经可以被8整除(成员e按8字节对齐)。这样一共使用了24字节。
内存布局图如下:
TestStruct4 的内存布局:
a b
1*** 1111
TestStruct5 的内存布局:
c d.a d.b e
1*** 1*** 1111 **** 11111111
注意3点:
(1)每个成员按照自己的方式对齐,并能最小化长度
(2)复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。
(3)对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐。
补充:对于数组,比如说char a[3],它的对齐方式和分别写3个char是一样的,也就是说它还是按1字节对齐;如果写成typedef char Arrary3[3],Arrary3这种类型的对齐方式还是按1字节对齐,而不是按它的长度。
但是不论类型是什么,对齐的边界一定是1、2、4、8、16、32、64......中的一个。
下面来说下大端模式和小端模式
大端模式:认为第一个字节是最高位字节,也就说按照从低地址到高地址的顺序存放数据的高位字节到低位字节。
小端模式:认为第一个字节是最低位字节,也就是说按照从低地址到高地址的顺序存放数据的低位字节到高位字节。
假设从内存地址0x0000开始有以下数据:
内存地址:0x0000 0x0001 0x0002 0x0003
对应数据:0x12 0x34 0x56 0x78
如果我们去读取一个地址为0x0000的4字节变量
若字节序位为小端模式,读出为:0x78563412
若字节序位为大端模式,读出为:0x12345678
一般来说:X86系列的CPU都是小端字节序,powerPC通常是大端字节序。
- int _tmain(int argc, _TCHAR* argv[])
- {
- char *sz = "0123456789";
- int *p = (int *)sz;
- cout<<hex<<*++p<<endl;
- }
运行结果为:37363534
分析:这里是小端字节序
地址从0x0000开始,那么sz在内存中的存储为:
内存地址: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09
对应的值: 0 1 2 3 4 5 6 7 8 9
对应的值: 48 49 50 51 52 53 54 55 56 57
对应的16进制:0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39
sz ++p
所以读取为:0x37363534
C/C++之内存对齐的更多相关文章
- C++内存对齐总结
大家都知道,C++空类的内存大小为1字节,为了保证其对象拥有彼此独立的内存地址.非空类的大小与类中非静态成员变量和虚函数表的多少有关. 而值得注意的是,类中非静态成员变量的大小与编译器内存对齐的设置有 ...
- C/C++: C++位域和内存对齐问题
1. 位域: 1. 在C中,位域可以写成这样(注:位域的数据类型一律用无符号的,纪律性). struct bitmap { unsigned a : ; unsigned b : ; unsigned ...
- C/C++ 知识点1:内存对齐
预备知识:基本类型占用字节 在32位操作系统和64位操作系统上,基本数据类型分别占多少字节呢? 32位操作系统: char : 1 int :4 short : 2 unsigned ...
- Windows+GCC下内存对齐的常见问题
结构/类对齐的声明方式 gcc和windows对于modifier/attribute的支持其实是差不多的.比如在gcc的例子中,内存对齐要写成: class X { //... } __attrib ...
- c++内存对齐
内存对齐原则: 1.数据成员对齐规则:struct, union的数据成员,第一个数据成员放在offset为0的地方,之后的数据成员的存储起始位置都是放在该数据成员大小的整数倍位置.如在32bit的机 ...
- C语言中内存对齐
今天一考研同学问我一个问题,一个结构体有一个int类型成员和一个char类型成员,问我这个结构体类型占多少个字节,我直接编个程序给他看结果.这个结构体占八个字节,咦,当时我蛮纳闷的,一个int类型四个 ...
- 内存对齐 和 sizeof小结
数据对齐(内存对齐)指该数据所在的地址必须是该数据长度的整数倍.X86CPU能直接访问对齐的数据,当它试图访问未对齐的数据时,会在内部进行一系列的调整,降低运行速度.数据对齐一般出现在结构体和类中,在 ...
- 解析C语言结构体对齐(内存对齐问题)
C语言结构体对齐也是老生常谈的话题了.基本上是面试题的必考题.内容虽然很基础,但一不小心就会弄错.写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的 ...
- C语言再学习之内存对齐
昨天看Q3的代码,看到有个_INTSAIZEOF的宏,着实晕了一阵.一番google后,终于明白,这个宏的作用是求出变量占用内存空间的大小,先看看_INTSAIZEOF的定义吧: #define _I ...
- C结构体中数据的内存对齐问题
转自:http://www.cnblogs.com/qwcbeyond/archive/2012/05/08/2490897.html 32位机一般默认4字节对齐(32位机机器字长4字节),64位机一 ...
随机推荐
- thinkphp开启事物的简单方法
使用thinkphp开启事务,ThinkPHP 3.2.2实现事务操作的方法: 开启事务: $User->startTrans() 提交事务: $User->commit() 事务回滚: ...
- thinkCMF----路由跳转
使用ThinkCMF的时候,在模板界面上,可能会用到一些自定义路由,ThinkCMF路由的基本配置与用法: ThinkCMF自带有路由美化的功能: 这种路由都是当你创建栏目或创建文章的时候,自动生成的 ...
- 9.13Django ORM那些事
2018-9-13 14:23:22 ORM那些事 参考 : https://www.cnblogs.com/liwenzhou/p/8660826.html 今天的都是ORM的查询 更详细进阶了! ...
- 8.22js前端!
2018-8-22 19:15:19 前端还有一部分就学完了预计这一星期赶一下就可以结束 然后愉快地进行Django框架!!!! 今天中午和呆呆一块吃的大盘鸡,下午和老表我们三个去了日月湖狼了一下午! ...
- Context对象还提供了相应的属性来调整线条及填充风格
创建一个Canvas画布的方法如下: 复制代码 代码如下: <canvas id=”canvas” width=”600” height=”400”></canvas> 可以在 ...
- cp命令取消提示的方法
Linux默认cp命令带参数-i如果有重复的文件会提示覆盖 查看cp别名 在大量复制的时候这个提示不友好,在脚本写复制命令也无法使用交互式输入 解决办法 1,修改别名 vi ~/.bashrc 注释掉 ...
- 0004python中的map,reduce,lambda,filter
编程实现:a[0]*b[0] + a[1]*b[1] +...+a[i]*b[j] >>> a=[1,2,3,4,5]>>> b=[6,7,8,9,0] >& ...
- free详解
用法: [oracle@server36 ~]$ free -help free: invalid option -- h usage: free [-b|-k|-m|-g] [-l] [-o] [- ...
- nginx分区域名转发 tp5域名分目录配置
需求 本来我们一般情况下都是域名abc.com解析到网站的根目录/root/public这种.但是客户突然提出了一个奇葩的需求,客户要求以后可能网站会增多,需要增加分区的功能,比如abc.com/wh ...
- Charles maplocal 时中文显示乱码问题
用Charles对request进行Map Local后,app上看返回的中文是乱码? 是Map Local的文件编码有问题?是Charles设置有问题?是电脑环境有问题?哈哈,都不是 你是Andro ...