纯属学习笔记: //此页面可以通过在dev_c++4.9.9.2运行,并通过小量的更改在其他IDE下运行. #if 0 Copyright (c) 2009,万维网 All rights reserved. 文件名称:memSet.h 摘 要:本文描述了内存对齐的各种概念和内存管理的其他知识点, 应用相应的程序示例 进行解释. 备 注: 本文资料收集于网络并通过作者整理. 此篇不考虑继承和虚函数虚表问题. 此类 问题分析详见下个版本. 当前版本:1.0 作 者:张旭 完成日期:2009年8月24日 #endif #if 0 【what and why】 即: 什么是字节对齐,为什么要对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问 可以从任何地址开始, 但实际情况是在访问特定类型变量的时候经常在特定的内存地址 访问, 这就需要各种类型数据按照一定的规则在空间上排列, 而不是顺序的一个接一个 的排放,这就是对齐. 即:对齐的作用和原因? 各个硬件平台对存储空间的处理上有很大的不同. 一些平台对某些特定类型的数据只能 从某些特定地址开始存取. 比如有些架构的CPU在访问一个没有进行对齐的变量的时候 会发生错误, 那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况, 但是最常见的是如果不按照适合其平台要求对齐数据存放进行对齐, 会在存取效率上带 来损失. 比如有些平台每次读都是从偶地址开始, 如果一个int型( 假设为32位系统 )如 果存放在偶地址开始的地方, 那么一个读周期就可以读出这32bit, 而如果存放在奇地址 开始的地方, 就需要2个读周期, 并对两次读出的结果的高低字节进行拼凑才能得到该 32bit数据.显然在读取效率上下降很多. 【4个重要概念】 1.数据类型自身的对齐值: 对于char型数据, 其自身对齐值为1; 对于short型为2; 对于int, float, double类型, 其自身对齐值为4单位字节. 2.结构体或者类的自身对齐值: 其成员中自身对齐值最大的那个值. 3.指定对齐值: #pragma pack (value)时的指定对齐值value. 4.数据成员, 结构体和类的有效对齐值: 自身对齐值和指定对齐值中小的那个值. 【有效对齐值拓展】 有效对齐值n是最终用来决定数据存放地址方式的值. 有效对齐n, 就是表示对齐在n上, 也就是说该数据的" 存放起始地址 % n = 0 ". 而数据结构中的数据变量都是按定义的 先后顺序来排放的. 第一个数据变量的起始地址就是数据结构的起始地址. 结构体的成 员变量要对齐排放, 结构体本身也要根据自身的有效对齐值圆整( 就是结构体成员变量 占用总长度需要是对结构体有效对齐值的整数倍, 结合下面例子理解 ). #endif #include<iostream> using std::cout; using std::endl; void newSet(); void newSet_P(); typedef struct { int id; //[0]....[3] double weight; //[7].....[15] 原则1 float height; //[16]..[19],总长要为8的整数倍,补齐[20]...[23] 原则3 }ZX; typedef struct { char name[2]; //[0],[1] int id; //[4]...[7] 原则1 double score; //[8]....[15] short grade; //[16],[17] ZX b; //[24]......[47] 原则2 }ZX_1; int main() { ZX_1 a; cout << sizeof( ZX_1 ) << " " << sizeof( ZX ) << endl; cout << "改变顺序的对比:" << endl; newSet(); cout << "利用编译指令#pragma pack( value )进行对比:" << endl; newSet_P(); system( "pause" ); return 0; #if 0 输出为:sizeof( ZX_1 ) = 48, sizeof(ZX) = 24 调整数据成员的结构就改变了结构的sizeof( value ); 例如:把ZX中的double weight;提到int id;前面的话就得到sizeof(ZX) = 16; #endif } void newSet() { typedef struct { int a; char b; short c; }NEW_1; typedef struct { char b; int a; short c; }NEW_2; typedef struct { int a; char b; short c; }NEW_0; struct { short c; char b; int a; }NEW_3; cout << sizeof( NEW_1 ) << endl; cout << sizeof( NEW_2 ) << endl; cout << sizeof( NEW_3 ) << endl; } void newSet_P() { #pragma pack( 1 ) typedef struct { int a; char b; short c; }NEW_1; typedef struct { char b; int a; short c; }NEW_2; typedef struct { int a; char b; short c; }NEW_0; struct { short c; char b; int a; }NEW_3; cout << sizeof( NEW_1 ) << endl; cout << sizeof( NEW_2 ) << endl; cout << sizeof( NEW_3 ) << endl; #pragma pack() } #if 0 【计算sizeof( value )必需遵循的原则】 数据成员对齐规则: 结构(struct)(或联合(union))的数据成员, 第一个数据成员放在offset为0的地方, 以 后每个数据成员存储的起始位置要从该成员大小的整数倍开始.( 如int在32位机为4字 节, 则要从4的整数倍地址开始存储) 结构体作为成员对齐规则: 如果一个结构里有某些结构体成员, 则结构体成员要从其内部最大元素大小的整数倍地 址开始存储. (struct a里存有struct b, b里有char, int , double等元素, 那b应该 从8的整数倍开始存储. ) 收尾工作: 结构体的总大小即sizeof的结果. 必须是其内部最大成员的整数倍. 不足的要补齐. 【在考虑内存对齐时的编程技巧】 在编程的时候要考虑节约空间的话, 那么我们只需要假定结构的首地址是0, 然后各个 变量按照上面的原则进行排列即可, 基本的原则就是把结构中的变量按照类型大小从小 到大声明,尽量减少中间的填补空间. 还有一种就是为了以空间换取时间的效率,我们显 示的进行填补空间进行对齐, 比如: 有一种使用空间换时间做法是显式的插入reserved成员 struct A { char a; char reserved[3];//使用空间换时间 int b; } reserved成员对我们的程序没有什么意义, 它只是起到填补空间以达到字节对齐的目的, 当 然即使不加这个成员通常编译器也会给我们自动填补对齐, 我们自己加上它只是起到显式的提醒作用. 【总结】 总之, 内存对齐方式就是把数据类型中的数据成员一个一个的排在内存中, 并且按照上面所 述的原则就行计算sizeof( type )的值. #endif