C++中的字节对齐分析
struct A
{
int a;
char b;
short c;
}; struct B
{
char a;
int b;
short c;
}; #pragma pack(2)
struct C
{
char a;
int b;
short c;
}; #pragma pack(1)
struct D
{
int a;
char b;
short c;
}; int _tmain(int argc, _TCHAR* argv[])
{ cout << sizeof(A) << " "<< sizeof B << " "<< sizeof C << " "<< sizeof D <<endl;
return ;
}
理论上来说,结构体A与B的大小应该都是一样的,造成这种原因的就是字节对齐引起来的。
2.为什么要字节对齐
3.编译器对字节对齐的一些规则
我从下面三条说明了编译器对字节处理的一些原则。当然除了一些特殊的编译器在处理字节对齐的方式也不一样, 这些情况我未碰到过,就不作说明了。
| 类型 | 对齐值(字节) |
| char | 1 |
| short | 2 |
| int | 4 |
| float | 4 |
| double | 8 |
d. 类、结构及成员的有效对齐字节值。有效对齐值=min(类/结构体/成员的自身对齐字节值,指定对齐字节值)。
有效对齐值决定了数据的存放方 式,sizeof 运算符就是根据有效对齐值来计算成员大小的。简单来说, 有效对齐其实就是要求数据成员存放的地址值能被有效对齐值整除,即:地址值%有效对齐值=0
4. 结合编译器分析示例
{
int a;
char b;
short c;
}
step : 根据第二条,首先为结构体选择对齐值:选择成员中最大的对齐值,即int a,对齐值为4
step : 再根据第四条原则,决定有效对齐值:即然没有手工指定对齐值,则使用默认的值:(windows 32平台)
step : int a 的有效地址值=min(,),(因为0x0000%=),这样a的地址就是从 0X0000~0x0003
step : char b 的有效对齐值=min(,),地址依次从0x0004 (因为Ox0004%=)开始,分配一个字节,地址段分配情况就是:0x0000~0x0004
step : short c 的有效对齐值=min(,),理论上说,分配的地址应该是连续的(从0x0005~0x00006),但是由于要求考虑到对齐的情况,所求要求地址段 偏移,这样就从0x0006(Offset+,因为0x0006%=)开始,分配2个字节的地址0x0006~0x0007.
目前为止,地址段的分配情况就是:0x0000~0x0007这样sizeof(A)的大小=0x0000~0x0007共8个字节大小,同时,%=0保证了Struct A的地址段与4成偶数倍。
接下来分析Struct B的大小,同样假设Struct B的起始地址为0x0000,分析步骤如下:
{
char a;
int b;
short c;
}
step : 确实结构体B对齐值:选择成员中最大的对齐值,即int a,对齐值为4
step : 确定手工指定对齐值,使用默认的值:(windows , VC6.0平台)
step : char a 的有效地址值=min(,),a的地址就是 0X0000(因为0x0000%=)
step : int b 的有效对齐值=min(,),地址依次从0x0004~0x0007 (因为Ox0004%=)开始,分配4个字节,目前j地址段分配情况就是:0x0000~0x0007
step : short c 的有效对齐值=min(,),c从0x0008~0x0009(因为0x0008%=)开始,偏移2个字节的地址0x0006~0x0007.
至止,地址段的分配情况就是:0x0000~0x0009共10个字节,但是Struct B的对齐值为4,这就要求地址地段再偏移2个字节,这样就是从0x0000~0x000B共12(因为12%=)个字节大小。这样,sizeof(B)=.
再来使用Pragma手工更改了字节对齐值的情况,先看看Struct C的定义:

struct C
{
char a;
int b;
short c;
};

在代码中,手工指定了对齐值为2个字节,分析步骤如下:
step : 确定结构体C对齐值:选择成员中最大的对齐值,即int a,对齐值为4
step : 确定手工指定对齐值,使用手工指定的值:
step : char a 的有效地址值=min(,),(因为0x0000%=),这样a的地址就是0x0000
step : int b 的有效对齐值=min(,),地址依次从0x0002~0x0005 (因为Ox0002%=)开始,分配4个字节,目前地址段分配情况就是:0x0000~0x0005
step : short c 的有效对齐值=min(,),由于要求考虑到对齐的情况,从0x0006(因为0x0006%=)开始,分配2个字节的地址0x0006~0x0007
目前为止,地址段的分配情况就是:0x0000~0x0007共8个字节,同时也保证了Struct C的对齐情况(2字节对齐,pragma()),sizeof(C)=.
请注意这种情况与Struct B的情况有区别,B的sizeof大小为12个字节,C的sizeof大小为8个字节。
最后分析#pragma pack(1)这种情况,这种情况非常简单,对齐值为1,因为1可以被任何数据整除,所以Struct D的成员变量存取顺序是连续的,这样就好办了,sizeof(D)=sizeof(int)+sizeof(char)+sizeof(short)=4+1+2=7 (比如从0x0000~0x0006)
总结
在考虑字节对齐时要细心,搞清楚几个重要的概念,如类型自身对齐值,手工对齐值以及有效对齐值,有效对齐值决定了最后的存取方式,有效对齐值等于类型自身对齐值与手工对齐值中较小的一个。理解了这一点,对sizeof运算符对类型或都结构的运算也彻底明白了。
以下测试实例 -------------------------------------------------------------------------------------------------
#include <iostream>
using namespace std;
// 没有指定对齐字节,则使用 结构体或类 中字节最大的类型字节值(前提是不超过 8 字节,当超过8字节,则采用4字节对齐)
struct A // 8 ->(8%4 == 0)-> 8
{
int a; //
char b;
short c;
}; struct B // 10 ->(10%4 != 0)-> 12
{
char a;
int b; //
short c;
}; struct C // 12 ->(12%8 != 0)-> 16
{
double b; //
char a;
short c;
}; struct D // 18 ->(18%8 != 0)-> 24
{
char a;
double b; //
short c;
}; struct E // 20 ->(20%4 == 0)-> 20
{
char a;
long double b; // (12 > 8) -> 采用 4 字节对齐, 和 #pragma pack(4) 效果一样
short c;
}; struct F // 16 ->(16%4 == 0)-> 16
{
long double b; //
char a;
short c;
}; #pragma pack(4) // 和 struct E 效果一样
struct G // 20 ->(20%4 == 0)-> 20
{
char a;
long double b; //
short c;
}; #pragma pack(2) // 会影响后续类型的对齐有效值,直到重新设置
struct H // 8 ->(8%2 == 0)-> 8
{
char a;
int b;
short c;
}; //#pragma pack(2)
struct I // 12 ->(12%2 == 0)-> 12
{
char a;
double b; //
short c;
}; #pragma pack(1)
struct J // 7 ->(7%1 == 0)-> 7
{
int a;
char b;
short c;
}; int main()
{
long double bb;
cout << sizeof(bb) << endl; // 12
// 输出结果为: 8 12 16 24 20 16 20 8 12 7
cout << sizeof(A) << " "<< sizeof(B) << " "<< sizeof(C) << " "<< sizeof(D) << " "<< sizeof(E) << " "<< sizeof(F) << " "<< sizeof(G) << " "<< sizeof(H) << " "<< sizeof(I) << " "<< sizeof(J) << endl;
return ;
}
C++中的字节对齐分析的更多相关文章
- C语言中的字节对齐以及其相关处理
首先,我们来了解下一些基本原理: 一.什么是字节对齐一个基本类型的变量在内存中占用n个字节,则该变量的起始地址必须能够被n整除,即: 存放起始地址 % n = 0,那么,就成该变量是字节对齐的;对于结 ...
- C语言字节对齐分析
1.前言 什么是字节对齐呢?现代计算机中的内存空间都是按字节(byte)划分的,从理论上讲似乎任何类型的变量的访问都可以从任何地址开始,但是实际情况是在访问特定变量的时候经常需要在特定的内存地址进行访 ...
- ACE的CDR中的字节对齐问题
大家应该都知道计算机中间都有字节对齐问题.CPU访问内存的时候,如果从特定的地址开始访问一般可以加快速度,比如在32位机器上,如果一个32位的整数被放在能被32模除等于0的地址上,只需要访问一次,而如 ...
- C++中的字节对齐
本博客(http://blog.csdn.net/livelylittlefish)贴出作者(三二一.小鱼)相关研究.学习内容所做的笔记,欢迎广大朋友指正! 字节对齐 1. 基本概念字节对齐:计算机存 ...
- C语言中的字节对齐
下面这个篇博客讲解很好 http://blog.csdn.net/meegomeego/article/details/9393783 总的来看分三类: 1. 不加 #pragma pack(n)伪指 ...
- 关于sizeof与#pragma pack 以及网络上关于字节对齐的一点感想
工作中面试中对于字节对齐基本上是必考一个知识点,而很多面试是网络上上原题.基本上背一背就可以写正确,而关于4字节对齐我相信很多人也只是一个基本地了解,对于一些题目就感觉有问题,而且很多blog后面仍然 ...
- c++内存中字节对齐问题详解
一.什么是字节对齐,为什么要对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址 ...
- stm32中使用#pragma pack(非常有用的字节对齐用法说明)
#pragma pack(4) //按4字节对齐,但实际上由于结构体中单个成员的最大占用字节数为2字节,因此实际还是按2字节对齐 typedef struct { char buf[3];//bu ...
- 关于C语言中结构体中的结构体成员导致的字节对齐问题
关于结构体的字节对齐是什么,就不赘述,再此附上一篇文章,介绍字节对齐:http://www.linuxsong.org/2010/09/c-byte-alignment/ 这里的结构体字节对齐的数据类 ...
随机推荐
- jQuery 初识
现在的项目中,用纯js的已经很少了,基本都是找这些好用的库,毕竟功能强大,学习简单,而且插件多. 今天就来学习下. 学习一个东西,就要先用安装入手. 这里我就直接从别的地方引入jquery库了, 引入 ...
- [3G/4G]3G/4G模块开发
转自:http://mywutuobang.blog.sohu.com/260073467.html 一. 修改内核支持3G模块,一般内核需要枚举相关的通讯模块,其接口为串口,其数据接口和通讯接口枚举 ...
- zip文件内存中解压读取
// 构造zip输入流 ZipInputStream zip = new ZipInputStream(fis,Charset.forName("gbk")); byte[] tm ...
- Android基础工具类重构系列一Toast
前言: 一直在考虑写一下Android实际项目中的一些总结,翻看CSDN博客,上一篇已经是一年多曾经. 本系列定位Android基础工具类重构.旨在记录实际项目中经经常使用到的一些工具类,比方Toas ...
- rails局部模板 render
<%= render partial: 'file' %> file是以_开头命名的文件,比如_cart.html.erb 这样就可以用render来调用了 还可以传参数 比如 rails ...
- KBEngine 服务器端-loginapp-协议构建、解析执行详细介绍
宏宏宏 由于 C++ 是静态语言,不能像 js 一样通过函数名字符串来直接执行函数,所以将 messageId 映射到可执行函数的复杂性大大提升:KBEngine 使用了一系列精巧的「宏」来解决这个问 ...
- s:debug标签的错误ConcurrentModificationException
搭建SSH的时候页面上加入<s:debug>标签后台出现 严重: Servlet.service() for servlet jsp threw exception java.util.C ...
- 【转】MFC 对话框Border属性设置(None、Thin、Resizing、Dialog Frame)
对话框的Border属性对应的值设置 Dialog Frame WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_CLIPSIBLINGS | DS_MODALFRAME ...
- 解决ERROR_INVALID_USER_BUFFER
我用CSocket编写了一个HTTP下载程序,接收数据大概如下: //...... //use CSocket class //send request... //...... ); ) { Zero ...
- C#获取并修改文件扩展名的方法
本文实例讲述了C#获取并修改文件扩展名的方法.分享给大家供大家参考.具体分析如下: 这里使用C#编程的方法改变文件扩展名的文件,必须使用Path类. Path类用来解析文件系统路径的各个部分.静态方法 ...