为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

字节对齐

2011-10-20 9页 pdf 24KB 21阅读

用户头像

is_032264

暂无简介

举报
字节对齐 以下是我对字节对齐问题的一些研究 有疑问可继续讨论 Alignment, Pack and Bit Field . . . 这两天仔细研究了一下关于字节对齐 Alignment的问题 现在写出来与大家分享与讨 论 欢迎指正 1. 为什么要对齐? 以32位的CPU为例 1664位同 它一次可以对一个32位的数进行运算 它的数据总线的宽度是32位 它从内存中一次 可以存取的最大数为32位 这个数叫CPU的字 word长 在进行硬件设计时 将存储体组织成32位宽 如每个存储体的宽度是8位 可用四 块存储体与CPU的32...
字节对齐
以下是我对字节对齐问题的一些研究 有疑问可继续讨论 Alignment, Pack and Bit Field . . . 这两天仔细研究了一下关于字节对齐 Alignment的问题 现在写出来与大家分享与讨 论 欢迎指正 1. 为什么要对齐? 以32位的CPU为例 1664位同 它一次可以对一个32位的数进行运算 它的数据总线的宽度是32位 它从内存中一次 可以存取的最大数为32位 这个数叫CPU的字 word长 在进行硬件时 将存储体组织成32位宽 如每个存储体的宽度是8位 可用四 块存储体与CPU的32位数据总线相连 这也是为什么以前的 386/486 计算机插SIMM30内存条(8位)时 必须同时插四条的原因 请参见下图 1 8 16 24 32 -------- ------- ------- -------- | long1 | long1 | long1 | long1 | -------- ------- ------- -------- | | | | long2 | -------- ------- ------- -------- | long2 | long2 | long2 | | -------- ------- ------- -------- | .... 当一个long型数 如图中long1在内存中的位置正好与内存的字边界对齐时 CPU 存取这个数只需访问一次内存 而当一个long型数 如图中long2在内存中的位置跨越 字边界时 CPU存取这个数就需多次访问内存 如 i960cx 访问这样的数需读内存三次 一个BYTE一个short一个BYTE由CPU的微代码执行 对 软件透明 所以在对齐方式下 CPU的运行效率明显快多了 这就是要对齐的原因 一般在编译器生成代码时 都可以根据各种CPU类型 将变量进行对齐 包括结构 struct中的变量 变量与变量之间的空间叫padding有时为了对齐在一个结构的最 后也会填入padding通常叫tail padding但在实际的应用中 我们确实有不对齐的 如在编通讯程序时 帧的结构 就不能对齐 否则会带来错误及麻烦 所以各编译器都提供了不对齐的选项 但由于这是 ANSI C中未规定的内容 所以各厂家的实现都不一样 下面是我们常用编译器的实现 2. 一般编译器实现对齐的方法 由于各厂家的实现不一样 这里涉及的内容只使用于Visual C++ 4.xBorland C++ 5.03.1及pRism x86 1.8.7 (C languange)其他厂家可能略有不同 每种基本数据类型都有它的自然对齐方式 Natural AlignmentAlign的值与该数据类型的大小相等 见下 Data Type sizeof Natural Align (signed/unsigned) char 1 1 short 2 2 long 4 4 . . . 同时用户还可以指定一个Align值 使用编译开关或使用#pragma当用户指定一个Alig n值 n 或编译器的缺省 时 每种数据类型的实际 当前 Align值定义如下 Actual Align = min ( n, Natual Align ) //公式 1 如当用户指定Align值为 2 时 char 的实际Align值仍为 1 short及long的实际Align值为 2当用户指定Align值为 1 时 所有类型的实际Align值都为 1 复杂数据类型 Complex or Aggregate type,包括 array, struct 及 union的对齐值定义如下 struct结构的Align值等于该结构所有成员的 Actual Align 值中最大的一个 Align 值 注意成员的Align值是它的实际Align值 array 数组的Align值等于该数组成员的 Actual Align 值 union 联合的Align值等于该联合最大成员的 Actual Align 值 同时当用户指定一个Align值时 上面的公式 1 同样起作用 只不过Natual Align应理解为当前的Actual Align 那么编译器是如何根据一个类型的Align值来分配存储空间 主要是在结构中的空 间 的呢 有如下两个规律 1 一个结构成员的offset等于该成员Actual Align值的整数倍 如果凑不成整数倍 就在其前加padding 2 一个结构的大小等于该结构Actual Align值的整数倍 如果凑不成整数倍 就在其后加paddingtail padding一个结构的大小在其定义时就已确定 不会因为其Actual Align值的改变而改变 例如有如下两个结构定义 #pragma pack(8) //指定Align为 8 struct STest1 { charch1; longlo1; charch2; } test1; #pragma pack() 现在 Align of STest1 = 4 , sizeof STest1 = 12 ( 4 * 3 ) test1在内存中的排列如下 FF 为 padding 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 FF FF FF 01 01 01 01 01 FF FF FF ch1 -- lo1 -- ch2 #pragma pack(2) //指定Align为 2 struct STest2 { charch3; STest1 test; } test2; #pragma pack() 现在 Align of STest1 = 2, Align of STest2 = 2 , sizeof STest2 = 14 ( 7 * 2 ) test2在内存中的排列如下 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 02 FF 01 FF FF FF 01 01 01 01 01 FF FF FF ch3 ch1 -- lo1 -- ch2 从以上可以看出 用户可以在任何需要的地方定义不同的align值 3. 不同编译器实现用户指定align值的方法 因为是 ANSI C 中未规定的内容 所以各厂家的方法都不一样 一般都提供命令行选项及使用#pragma 命令行选项对所有被编译的文件都起作用 #pragma则是ANSI C特别为实现不同的编译器及平台特性而规定的预处理器指令 Preprocessor下面主 要讲一下#pragma的实现 Visual C++ VC使用 #pragma pack( [n] )其中 n 可以是 1, 2, 4, 8, 16, 编译器在遇到一个#pragma pack(n)后就将 n 当作当前的用户指定aling值 直到另一个#pragma pack(n)当遇到一个不带 n 的 pack 时 就恢复以前使用的align值 Borland C++BC使用 #pragma option -an 在 BC 5.0 的Online Help中没有发现对#pragma pack的支持 但发现在其系统头文件中使用的都是#pragma pack pRism x86 使用 #pragma pack( [n] ) 但奇怪的是 C 文件与 C++ 文件生成的代码不一样 有待进一步研究 gcc960 使用 #pragma pack n 及 #pragma align n 两个开关的意义不一样 并且相互作用 比较复杂 但同时使用 #pragma pack 1 及 #pragma align 1 可以实现与Visual C++中 #pragma pack(1) 一样的功能 其他编译器的方法各不相同 可参见手册 如果要使用不同的编译器编译软件时 就要针对不同的编译器使用不同的预处理器指令 4. 使用 #pragma pack 或其他开关 需注意的问题 1. 为了保证执行速度 尽量不使用#pragma pack 2. 不同的编译器生成的代码极有可能不同 一定要查看相应手册 并做实验 3. 需要加pack的地方一定要在定义结构的头文件中加 不要依赖命令行选项 因为如果很多 人使用该头文件 并不是每个人都知道应该pack特别是为别人开发库文件时 如果一个 库函数使用了struct作为其参数 当调用者与库文件开发者使用不同的pack时 就会造成 错误 而且该类错误很不好查 在VC及BC提供的头文件中 除了能正好对齐在四字节上的 结构外 都加了pack否则我们编的Windows程序哪一个也不会正常运行 4. 在 #pragma pack(n) 后一定不要include其他头文件 若包含的头文件中改变了align值 将产生非预期结果 VC中提供了一种安全使用pack的方法 #pragma pack( [ push | pop ], n ) #pragma pack( push, n )将当前的align值压入编译器的一个内部堆栈 并使用 n 作为当前的align值 而#pragma pack(pop)则将内部堆栈中的栈顶值作为当前的align值 这样就保证了嵌套pack时的正确 5. 不要多人同时定义一个数据结构 在多人合作开发一个软件模块时 为了保持自己的编程 风格 每个人都要对同一结构定义一份符合自己风格的数据类型 当两个人之间需要传递 该数据结构时 如果两个人的 pack 值不一样 就会产生错误 该类错误也很难查 所以 为了安全起见 我们还是舍弃一些 自己的风格吧 5. 关于位域 Bit Field 在 ANSI C 中规定位域的类型只能为 signed/unsigned int但各厂家都对其进行了扩展 类型可以是 char, short, long 等 但其最大长度不能超过int的长度 即32位平台时为32位 16位平台时为16位 位域 存储空间的分配也与各编译器的实现有关 而且与Little Endian(x86,i960),Big Endian(680x0,PowerPc)有关 所以在定义位域时要对不同的编译器进行不同的支持 如在VC中规定 如果两个连续位域的类型不一样 或位域的长度为零 编译器将进 行对齐 在VC中是这样 其他编译器就可能不是这样 这属于各厂家不同的实现问题 ANSI C 中没有进行规定 所以如果涉及到位域问题 一定要查看手册 6. 附例 以下结果均在VC++4.xBC++5.0,3.1pRism x86 1.8.7(C Language) 进行过验证 其中因为BC++ 3.1 是16位的 所以只有pack(1),pack(2)有效 例中定义了如下几个结构 typedef struct tagSLong { char chMem1; char chMem2; char chMem3; unsigned short wMem4; unsigned long dwMem5; unsigned short wMem6; char chMem7; }SLong; typedef struct tagSShort { char chMem1; unsigned short wMem2; char chMem3; }SShort; typedef union tagun { char uChar; unsigned short uWord; }un; typedef struct tagComplex { char chItem1; SLong struItem2; unsigned long dwItem3; char chItem4; un unItem5; }Complex; 测试时对每个结构的成员按 1 2 3 ... 依次进行赋值 FF 为 Padding下面列出了每个结构的sizeAlign的大小及其空间分配 1. Now the Align(Pack) size is 8 sizeof(SLong) = 16 Alignment of (SLong) = 4 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 02 03 FF 04 00 FF FF 05 00 00 00 06 00 07 FF sizeof(SShort) = 6 Alignment of (SShort) = 2 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 FF 02 00 03 FF sizeof(Complex) = 28 Alignment of (Complex) = 4 [Notice the alignment of (SLong) = 4 and (un)=2 ] 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 FF FF FF 01 02 03 FF 04 00 FF FF 05 00 00 00 06 00 07 FF 08 00 00 00 09 FF 0A 00 sizeof(SLong[2]) = 32 Alignment of (SLong[2]) = 4 [Notice the alignment of (SLong) = 4 ] 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 02 03 FF 04 00 FF FF 05 00 00 00 06 00 07 FF 01 02 03 FF 04 00 FF FF 05 00 00 00 06 00 07 FF sizeof(un) = 2 Alignment of (un) = 2 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 02 00 2. Now the Align(Pack) size is 4 sizeof(SLong) = 16 Alignment of (SLong) = 4 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 02 03 FF 04 00 FF FF 05 00 00 00 06 00 07 FF sizeof(SShort) = 6 Alignment of (SShort) = 2 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 FF 02 00 03 FF sizeof(Complex) = 28 Alignment of (Complex) = 4 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 FF FF FF 01 02 03 FF 04 00 FF FF 05 00 00 00 06 00 07 FF 08 00 00 00 09 FF 0A 00 sizeof(SLong[2]) = 32 Alignment of (SLong[2]) = 4 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 02 03 FF 04 00 FF FF 05 00 00 00 06 00 07 FF 01 02 03 FF 04 00 FF FF 05 00 00 00 06 00 07 FF sizeof(un) = 2 Alignment of (un) = 2 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 02 00 3. Now the Align(Pack) size is 2 sizeof(SLong) = 14 Alignment of (SLong) = 2 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 02 03 FF 04 00 05 00 00 00 06 00 07 FF sizeof(SShort) = 6 Alignment of (SShort) = 2 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 FF 02 00 03 FF sizeof(Complex) = 24 Alignment of (Complex) = 2 [Notice the alignment of (SLong) = 2 and (un) = 2 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 FF 01 02 03 FF 04 00 05 00 00 00 06 00 07 FF 08 00 00 00 09 FF 0A 00 sizeof(SLong[2]) = 28 Alignment of (SLong[2]) = 2 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 02 03 FF 04 00 05 00 00 00 06 00 07 FF 01 02 03 FF 04 00 05 00 00 00 06 00 07 FF sizeof(un) = 2 Alignment of (un) = 2 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 02 00 4. Now the Align(Pack) size is 1 sizeof(SLong) = 12 Alignment of (SLong) = 1 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 02 03 04 00 05 00 00 00 06 00 07 sizeof(SShort) = 4 Alignment of (SShort) = 1 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 02 00 03 sizeof(Complex) = 20 Alignment of (Complex) = 1 [Notice the alignment of (SLong) = 1 and (un) = 1] 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 01 02 03 04 00 05 00 00 00 06 00 07 08 00 00 00 09 0A 00 sizeof(SLong[2]) = 24 Alignment of (SLong[2]) = 1 [Notice the alignment of (SLong) = 1 ] 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 01 02 03 04 00 05 00 00 00 06 00 07 01 02 03 04 00 05 00 00 00 06 00 07 sizeof(un) = 2 Alignment of (un) = 1 00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- -- 02 00 关于pack使用的几点建议及需注意的问题 huyuelin 3520 作于 04-17 00:56 0. 由于各种编译器对pack的实现各不相同 建议定义几个头文件(参照microsoft的做法) poppack.h, pshpack1.h, pshpack2.h ... 其中poppack.h用于恢复编译器的缺省pack值 其大略定义如下 #if _HA_WIN32 //for Visual C++ #pragma pack() #elif _HA_GNU //for gcc960 #pragma pack #pragma align 0 #elif ... //for any more pshpack1(n).h用于指定编译器的pack值为 n , 其大略定义如下 #if _HA_WIN32 //for Visual C++ #pragma pack(1)//n #elif _HA_GNU //for gcc960 #pragma pack 1//n #pragma align 1//n #elif ... //for any more 使用时 在需要pack的地方加上 #include "pshpack1.h" , 在需要恢复pack的地方加上 #include "poppack.h" . 使用这种头文件的方式有如下几点好处 1. 在需要pack的头文件中不需要再对不同的编译器做处理 使得该头文件 比较整洁 2. 便于维护 当需要增加对其他编译器的支持或对现有pack指令进行修改 时 只需修改poppack.h等几个头文件 1. 为了保证执行速度 在没有必要的地方不要使用#pragma pack不要只为了节省空间而使用 BYTE等类型 其实数据的空间是减少了 但代码的空间却变大了 如本来只需一条指令的 地方可能需三四条指令 即影响了执行速度 又增加了空间 得不偿失 如果必须使用BY TE等类型 尽可能将其在结构中排成自然对齐 2. 不同的编译器生成的代码极有可能不同 一定要查看相应手册 并做实验 如对于如下结 构定义 struct SLanDest { WORD wTag; MACADDR addr; }; 该结构在VC下是不加padding的 但在pRism下就加了padding 3. 需要加pack的地方一定要在定义结构的头文件中加 不要依赖命令行选项 因为如果很多 人使用该头文件 并不是每个人都知道应该pack特别是为别人开发库文件时 如果一个 库函数使用了struct作为其参数 当调用者与库文件开发者使用不同的pack时 就会造成 错误 而且该类错误很不好查 在VC及BC提供的头文件中 除了能正好对齐在四字节上的 结构外 都加了pack否则我们编的Windows程序哪一个也不会正常运行 4. 在 #include "pshpack1.h" 后一定不要include其他头文件 若包含的头文件中改变了align值 如包含了#include "poppack.h"将产生非预期结果 5. 不要多人同时定义一个数据结构 在多人合作开发一个软件模块时 为了保持自己的编程 风格 每个人都要对同一结构定义一份符合自己风格的数据类型 当两个人之间需要传递 该数据结构时 如果两个人的 pack 值不一样 就会产生错误 该类错误也很难查 所以 为了安全起见 我们还是舍弃一些 自己的风格吧 6. 何时需要加pack? 在编写通信时 通信协议的帧结构 对于所有跨CPU的协议 都应理解为通信协议 如邮箱通信 主机与主机通过通信线路进行通信等 编写硬件驱动程序时 寄存器的结 构 这两个地方都需要加pack1即使看起来本来就自然对齐的 也要加pack以免不同 的编译器生成的代码不一样 如 2. 中的例子 对于运行时只与一个CPU有关的结构 为了提高执行速度 请不要加pack
/
本文档为【字节对齐】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
热门搜索

历史搜索

    清空历史搜索