【转载】【内存对齐(二)】__declspec( align(#) )的用法和大小计算
转自:http://www.cppblog.com/deercoder/archive/2011/03/13/141747.html
感谢作者!
在上面讲到了关于pack的内存对齐和计算方法,这里继续讲实现内存对齐的另一种方式:__declspec( align(#) )
__declspec( align(#) )和#pragma pack( n )有密切联系。
当一个变量或结构体同时受两者影响时,前者的优先级高。
成员的地址决定于前者及后者,其要么是前者的倍数,要么是后者的倍数,要么是成员的大小的倍数,取最小。
结构体最后的大小于前者有关,其要么是前者的倍数,要么是结构体中最大偏移量的倍数,取最大。
要算出最后结果,必须知道两者的值或缺省值。
下面举一个例子来详细的分析:
#include "stdafx.h"
#include <stdlib.h>
//using namespace std;
#pragma pack( push, 4 )
__declspec( align(32) )struct D
{
int i1;
double d1;
int i2;
int i3;
};
int main()
{
cout << "sizeof(int) = "<<sizeof(int) << endl;
cout << "sizeof(char) = " << sizeof(char) << endl;
cout << "sizeof(double) = " << sizeof(double) << endl;
cout << sizeof(D) << endl;
system("PAUSE");
return 0;
}
这段代码在VS 2010中的运行结果是,sizeof(D)的大小为32,而在Dev C++,C-Free 5.0以及gcc中的结果都似乎20(转载时编辑:QT下mingw编译器也为20)。下面我们来着重讲讲关于__declspec( align(#) )的用法:
正如前面所说的,当有__declspec( align(#) )和pack的时候,__declspec( align(#) )的优先级要高些。所以对于上面这个例子,我们首先来计算出来每一个的大小。
- 成员的地址如何取?
规则:成员的地址要取pack(n),__declspec( align(m) ),以及成员自身大小这三者之间的最小值,也就是,min(n,m,sizeof(成员变量类型)),那么我们可以对每一个结构体的成员都进行分析。
第一个为int类型,占据4B,所以地址是[0~3].
第二个为double类型,它的地址要根据min(4,32,sizeof(double))来判断,所以应该是4的倍数,也就是相邻着int类型的i1存放。地址是[4~11]。
第三个为int类型,占据4B,同样应该是4的倍数,地址是[12~15].
第四个为int类型,占据4B,地址为[16~19].
从而总的地址是从[0~19]连续存放的20个字节,那么是否sizeof(D)的大小就是20呢?
经过测试,我们可以看到,在VS 2010中,结果是32,why?
这就要用__declspec( align(#) )来解释了。也就是下面第二点的内容。
- 结构体最后的大小如何决定?
规则:结构体最后的大小与__declspec( align(m) )有关,其要么是它的倍数,要么是结构体中最大偏移量的倍数,取最大。
根据这个规则,这里align是32,而结构体中最大的是double类型,也就是应该是max(32,8)=32,所以最后结构体的大小应该是32的倍数,而明显上面我们看到的实际大小是20B,从而需要扩展到32B。
在这里,就体现了__declspec( align(m) )的强大作用!
同样的,为了体现该语句的作用,我们去掉这个语句,运用我们前面一节内容的知识,来计算并测试sizeof(D),最终不论是在VS 2010还是Dev C++中,运行的结果都是上面我们所预测的20B。
OK,下面回到最后的疑问,也就是前面我们提出的,为何加入了__declspec( align(m) )语句之后,在DevC++和VS 2010的结果不同?
实际上,对于这些内存对齐的处理,不同的编译器可能采取不同的处理,就像前面一节中所说的,我将pack误用为package,导致根本没有达到按照我要求的字节对齐的目的,而且编译器根本不提供任何警告信息。那么,这里合理的解释是:Dev C++不支持这种用法。
通过查阅资料,参照这篇文章【 SSE指令介绍及其C、C++应用 】(http://blog.csdn.net/delphihero/archive/2006/09/24/1270069.aspx),我们可以看到作者有这么一段话:
“接下来我举一个例子来说明SSE的指令函数是如何使用的,必须要说明的是我以下的代码都是在VC7.1的平台上写的,不保证对其它如Dev-C++、Borland C++等开发平台的完全兼容。”
“这里要注意一下,我使用了__declspec(align(16))做为数组定义的修释符,这表示该数组是以16字节为边界对齐的,因为SSE指令只能支持这种格式的内存数据。
我们在这里看到了SSE算的强大,相信它会成为多媒体程序员手中用来对付无穷尽流媒体数据的一把利剑。我后面还会写一些关于SSE算法更复杂应用的文章,敬请关注,感谢您抽时间阅读!
”
从这篇文章我们可以看到,SSE指令集的情况下,在VC 7.1下才支持__declspec(align(16))这种用法,而对于其他平台不一定有效。而前面我们使用的Dev C++以及C-Free,都是基于g++或者MinGW,不一定会支持这种方式,或者说,不一定按照这种内存对齐的建议来做,也就造成了结果的不同。
下面我们来继续探讨结构体中有结构体的情况。
先看看下面这段代码:
#include "stdafx.h"
#include <stdlib.h>
//using namespace std;
#pragma pack( push, 4 )
__declspec( align(32) )struct D
{
int i1;
double d1;
int i2;
int i3;
};
__declspec( align(16) ) struct E
{
int i1;
D m_d;
int i2;
};
int main()
{
cout << "sizeof(int) = "<<sizeof(int) << endl;
cout << "sizeof(char) = " << sizeof(char) << endl;
cout << "sizeof(double) = " << sizeof(double) << endl;
cout << sizeof(D) << endl;
cout << sizeof(E) << endl;
system("PAUSE");
return 0;
}
最后运行的结果是sizeof(E)为96,为何会是这个结果呢?我们来详细讲解下。
对于结构体E,第一个元素为int类型,所以占据[0~3]地址单元。
第二个元素是一个结构体,该结构体由于受上面__declspec( align(32) )的影响,优先级高,所以起始地址是32的倍数,而且大小为32B,从而应该放置在[32~63]单元处。
最后一个是int类型的变量,大小为4,所以应该是4的倍数,地址为[64~67]。
故结构体E的大小应该是从[0~67],占据68B,而由于前面还有限制__declspec( align(16) ),同时成员变量的最大偏移是sizeof(D)=32,所以我们最后这个结构体的大小应该是他们中最大值的倍数,也就是32的倍数,68向上取32的倍数应该是96.故结果为96.
最后仍然是上面平台的问题,在Dev C++和G++下面的结果不同,原因上面解释了。
MSDN:
“The sizeof value for any structure is the offset of the final member, plus that member's size, rounded up to the nearest multiple of the largest member alignment value or the whole structure alignment value, whichever is greater.”
中文:
“sizeof的结果都是结构体中最后的一个成员变量加上它的大小,再加上一个填充容量(padding),这个填充大小是成员变量最大的一个对齐参数或整个结构体的对齐参数的倍数,取哪个决定于哪个对齐参数较大”
ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_vclang/html/e4209cbb-5437-4b53-b3fe-ac264501d404.htm
ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_vclang/html/9cb63f58-658b-4425-ac47-af8eabfc5878.htm
P.S.:上面是关于内存对齐的研究,如有谬误,欢迎指出!
附参考资料和拓展:
1. #pragma pack :http://blog.sina.com.cn/s/blog_492aa57901008y3h.html2. #pragma pack( n )和__declspec( align(#) ) 的偏移量计算方法: http://blog.csdn.net/whoismickey/archive/2009/03/28/4032155.aspx3. #pragma pack(push,1) (pop) :http://blog.csdn.net/jiang1013nan/archive/2009/11/25/4861248.aspx4. 关于pragma pack的用法(四) C++中的内存对齐问题: http://www.cppblog.com/xczhang/archive/2007/12/23/39396.html
5. SSE指令介绍及其C、C++应用:http://blog.csdn.net/delphihero/archive/2006/09/24/1270069.aspx6. c++中__declspec用法总结: http://sealbird.javaeye.com/blog/855096
【转载】【内存对齐(二)】__declspec( align(#) )的用法和大小计算的更多相关文章
- golang内存对齐分析(转载)
问题 type Part1 struct { a bool b int32 c int8 d int64 e byte } 在开始之前,希望你计算一下 Part1 共占用的大小是多少呢? func m ...
- c++内存对齐 转载
转载自http://blog.csdn.net/chengonghao/article/details/51674166 例子举的特别好 很多文章大概都有像这样的结论: 1. 数据项只能存储在地址是数 ...
- Windows+GCC下内存对齐的常见问题
结构/类对齐的声明方式 gcc和windows对于modifier/attribute的支持其实是差不多的.比如在gcc的例子中,内存对齐要写成: class X { //... } __attrib ...
- C语言内存对齐详解
一.字节对齐基本概念 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型 ...
- 【转】C/C++ struct/class/union内存对齐
原文链接:http://www.cnblogs.com/Miranda-lym/p/5197805.html struct/class/union内存对齐原则有四个: 1).数据成员对齐规则:结构(s ...
- [工作积累] GCC 4.6 new[] operator内存对齐的BUG
对于用户没有定义dctor(包括其所有成员)的类来说, new CLASS[n] 可能会直接请求sizeof(CLASS)*n的空间. 而带有dctor的 类, 因为delete[]的时候要逐个调用析 ...
- VC++平台上的内存对齐操作
我们知道当内存的边界正好对齐在相应机器字长边界上时,CPU的执行效率最高,为了保证效率,在VC++平台上内存对齐都是默认打开的,在32位机器上内存对齐的边界为4字节:比如看如下的代码: struct ...
- vs2012 函数参数内存对齐引发编译错误
编译一个游戏库时,遇到个奇怪的问题.一个模板函数,形参是按值传入的.编译时实参是内存对齐过的,无法通过,引发类似下面的错误: error C2719: '_Val': formal parameter ...
- NumPy-快速处理数据--ndarray对象--多维数组的存取、结构体数组存取、内存对齐、Numpy内存结构
本文摘自<用Python做科学计算>,版权归原作者所有. 上一篇讲到:NumPy-快速处理数据--ndarray对象--数组的创建和存取 接下来接着介绍多维数组的存取.结构体数组存取.内存 ...
随机推荐
- lintcode:在O(1)时间复杂度删除链表节点
题目: 在O(1)时间复杂度删除链表节点 给定一个单链表中的表头和一个等待被删除的节点(非表头或表尾).请在在O(1)时间复杂度删除该链表节点.并在删除该节点后,返回表头. 样例 给定 1->2 ...
- C#五层架构
UI Business Logic Layer Business Rule Layer Data Access Layer Data Definition Layer 简单工厂模式 simple fa ...
- 使用WM_COPYDATA跨进程发送数据
进程之间通讯的几种方法: 在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯.常用的方法有 使用内存映射文件 通过共享内存DLL共享内存 使用SendMessage向另一进程发送WM_ ...
- MVVM 代码记录
一.XML <Page x:Class="MVVM.MainPage" xmlns="http://schemas.microsoft.com/winfx/20 ...
- NSDictionary 初始化
NSDictionary *dic1=[NSDictionary dictionaryWithObject:@"1" forKey:@"a"]; ...
- Android 对话框弹出位置和透明度的设置
在Android中 我们经常会用AlertDialog来显示对话框.通过这个对话框是显示在屏幕中心的.但在某些程序中,要求对话框可以显示在不同的位置.例如,屏幕的上 方或下方.要实现这种效果.就需要获 ...
- QTP鼠标点击和浏览器事件的动态切换
今天在群里有人问到一个问题,我觉得应该会有很多人会碰到,今天根据自己的思路把这个解决方案整理出来,供自己和大家参考 需求描述: 当输入一个身份证号码的时候,这个号码所对应的数据会被加载到所属的省和市的 ...
- jQuery 表单验证插件 jQuery Validation Engine 使用
jQuery 表单验证插件 jQuery Validation Engine 使用方式如下: 1.引入头文件(注意一定要把jQuery放在前面),指定使用 jQuery Validation Engi ...
- UIColor的用法
UIColor,CGColor,CIColor的区别和联系 layer.shadowColor = [UIColor redColor].CGColor; 这个是今天用到的.顺便总结一下. 1.UIC ...
- Windows 7下配置JDK环境变量
安装jdk1.8版本(下载链接:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) ...