C/C++字节对齐问题

From: http://blog.csdn.net/alex_bn/article/details/6770788

 

首先来看下在C/C++中定义如下的结构体,然后对他们分别进行sizeof()运算,看看结果会如何

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

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

#pragma pack(push,2)     /*指定2字节对齐*/
struct C
{
     
char b;
     
int a;
     
short c;
};
#pragma pack (pop)      /*还原默认字节对齐*/

#pragma pack (push,1)   /*指定1字节对齐*/
struct D
{
     
char b;
     
int a;
     
short c;
};
#pragma pack (pop)      /*还原默认字节对齐*/

各个结构体内的变量只是顺序不一样!各变量字节和为1+4+2=7个字节,而对各个结构体进行sizeof()运算所得的结果却是不一样的:sizeof(A)==8   sizeof(B)==12   sizeof(C)==8   sizeof(D)==7

非常有趣吧?造成这个差异的是什么原因呢?其实就是下面要讲的C/C++中的字节对齐

什么是字节对齐以及为什么要对齐?

现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是字节对齐

而各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。

对齐的实现

通常,我们写程序的时候,不需要考虑对齐问题。编译器会替我们选择时候目标平台的对齐策略。当然,我们也可以通知给编译器传递预编译指令而改变对指定数据的对齐方法。但是,正因为我们一般不需要关心这个问题,所以因为编辑器对数据存放做了对齐,而我们不了解的话,常常会对一些问题感到迷惑,本文上面的例子就如此。

对齐的算法

由于各个平台和编译器的不同,现以vc6.0(32位x86平台)为例子,来讨论上例中编译器对struct数据结构中的各成员如何进行对齐的。这里面有四个概念值:

1.数据类型自身的对齐值:就是上面交代的基本数据类型的自身对齐值。

2.指定对齐值:#pragma pack(push,value)时的指定对齐值value。

3.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。

4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小的那个值。

有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍,结合下面例子理解)。这样就不能理解上面例子的值了。

例子分析:

结构体B分析

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

假设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值,在vc6.0环境下,该值默认为4。

第一个成员变量b的自身对齐值是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.第二个成员变量a的自身对齐值为4,所以有效对齐值也为4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,复核0x0004%4=0,   且紧靠第一个变量。第三个变量c,自身对齐值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存放的都是结构体B的内容。再来看看数据结构B的自身对齐值为其变量中最大对齐值(这里是a)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求,0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B共有12个字节,sizeof(B) == 12

结构体C分析

#pragma pack(push,2)     /*指定按2字节对齐*/
struct   C
{
     
char b;
     
int a;
     
short c;
};
#pragma pack(pop)       /*还原默认字节对齐*/

第一个变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合0x0000%1=0;第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续字节中,符合0x0002%2=0。第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放在0x0006、0x0007中,符合0x0006%2=0。所以从0x0000到0x00007共八字节存放的是C的变量。又C的自身对齐值为4,所以C的有效对齐值为2。又8%2=0,C只占用0x0000到0x0007的八个字节。所以sizeof(C) == 8

最后来说一下,如果指定字节对齐为1字节,那么这个结构体实际占用的字节大小就是结构体内各变量真实占用字节大小之和!


(转载)http://wzoot.blog.163.com/blog/static/416388002007111210305852/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/406746.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一起来看流星雨剧情简介/剧情介绍/剧情分集介绍第六集

云海为了应战雨荨的挑战,去学院雕像拜传说中的考神,祈祷自己能通过考试。上官、叶烁为了帮助云海通过考试,特意买来了高级的作弊系统。考场上,云海突然傲气发作,一把扔掉了作弊的笔,不巧正好落在雨荨的座位…

Sublime Text添加插入带当前时间说明

作者:木尘 日期:2014-11-25 插件实现插入带时间功能的说明: 1. 创建插件: Tools → New Plugin: import datetime import sublime_plugin class AddInfoCommand(sublime_plugin.TextCommand):def run(self, edit):self.view.run_c…

VC调用C#写的DLL

From: http://hi.baidu.com/ocnc/blog/item/2c7fc45c1d1f1051fbf2c0ea.html VC中调用C#写的DLL时,对C#这边的DLL有要求,即要在.NET这边以COM方式生成DLL; 步骤: 1.创建类库;类库的创建必须分为接口和实现类&#xff…

React开发(144):Git Commit 规范

#### feature: 开发新的功能 fix: 修复bug refactor: 代码重构 docs: 文档修改 style: 代码格式修改, 注意不是 css 修改 perf: 改善性能 build: 变更项目构建或外部依赖(例如scopes: webpack、gulp、npm等) revert: 代码回退 比如&#…

[转]程序实现调用迅雷(以C#为例)

1.首先导入DLL 添加引用 -> COM -> ThunderAgent 1.0 Type Library(前提是已安装迅雷) 或者C:\Program Files\Thunder Network\Thunder\ComDlls\ThunderAgent_Now.dll(路径可能有所不同) 2.代码: THUNDERAGENTLi…

PHP Yii框架中使用smarty模板

第一种方法 按照YII系统的办法生成视图觉得有点麻烦,觉得用smarty更省事。尝试着把smarty模板加进来了。 date_default_timezone_set("PRC"); class PlaceController extends CController { protected $_smarty; function __construct(){ parent::__const…

COM应用实例--获取桌面墙纸路径

COM应用实例--获取桌面墙纸路径&#xff1a;Win32控制台程序(C) /*功能&#xff1a; 获取桌面墙纸的路径&#xff0c;并建立一个该墙纸的快捷方式保存在指定路径说明&#xff1a; COM应用实例, Win32控制台程序 */ #include <iostream> #include <afx.h> #include …

React开发(145):目录规范:

config ├── config.js umi 配置目录 ├── plugin.config.js webpack插件配置目录 └── router.config.js 路由配置目录 src: 静态资源目录: assets 公共组件目录&#xff1a;common 业务组件目录: components 主框架目录&#xff1a;layouts 国际化目录&…

献给网页开发者的20款Firefox插件

毫无疑问&#xff0c;比起其他浏览器&#xff0c;Chrome虽有着明显的优势&#xff0c;但是它的劲敌Firefox却是很多前端攻城师和研发人员的挚爱&#xff0c;为了尽可能地提高用户体验&#xff0c;Firefox的社区在插件开发和运用上有着丰富的经验&#xff0c;说它是网站开发者的…

VC6重载字体对话框,去除颜色下拉框

为了去除字体对话框中的颜色下拉框&#xff0c;我们需要重载字体对话框类&#xff1a;CFontDialog 1. 创建一个MFC Class&#xff0c;继承自CFontDialog: // MyFontDialong.h : header file //// CMyFontDialong dialogclass CMyFontDialong : public CFontDialog {DECLARE_DY…

Mac系统下如何使用命令行方式启动MySQL

首先&#xff0c;先去官网下载MySQL&#xff0c;选择Mac版本的DMG格式的文件。 官网地址为&#xff1a;dev.mysql.com/downloads/mysql/5.7.html 然后下载后&#xff0c;直接双击对应的dmg格式的文件进行安装即可。接下来&#xff0c;我们可以先看下如何在系统偏好设置中启动M…

React开发(146):开发规范

1&#xff0c;编辑器尽量用 VSCode&#xff0c;然后装 Prettier 插件 简单的代码规范 2&#xff0c;缩进2个空格&#xff0c;如果是tab 也设置成2个空格 3&#xff0c;命名时尽量用英文&#xff0c;文件名&#xff0c;组件名等&#xff0c;单词多的可以大写简写。 4&#xff0…

禁止微信公众号页面上下滑动

data(){return {handler: function(e) {e.preventDefault();}, //阻止body滑动} }, methods: {closeTouch: function() {document.getElementsByTagName("body")[0].addEventListener("touchmove", this.handler, { passive: false }); //阻止默认事件}, o…

权限管理系统系列之序言

权限管理系统我相信每个系统都会需要&#xff0c;所以设计一个好的权限管理系统会在系统开发中起到至关重要的作用&#xff0c;节省人力成本和资源&#xff0c;可以不用每次都为权限设计煞费心机。可能每个公司对权限的设计大体相同&#xff0c;略有不同&#xff0c;但是都会不…

linux借助expect完成自动登录

From: http://www.cnblogs.com/sunss/archive/2010/12/23/1914763.html 在工作中&#xff0c;我需要把远程服务器上的文件&#xff0c;拷贝到本地&#xff0c;可是scp的时候必须输入密码&#xff0c;用here文档又不能实现&#xff0c;终于找到一个好东东&#xff0c;expect ,可…

android之隐示意图跳转启动另一个activity

主面板布局&#xff1a;layout/activity_main.xml <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android" android:layout_width"match_parent" android:layout_height"match_parent" android:orientation"…

linux培训课程第六天:ppt以及笔记

第六天授课大纲介绍&#xff1a;第十讲&#xff1a;引导服务器dhcp和NIS这里dhcp服务比较简单,说明一下&#xff0c;首先将dhcp做成功,然后有个dhcp中继,做起来很简单,最主要大家要理解&#xff0c;中继是处在怎样的环境中&#xff0c;你怎样去搭配,这个明白了&#xff0c;下面…

微信公众号 H5 页面中点击一个按钮调起手机的电话拨打功能

1、在H5页面中调起手机的电话拨打功能其实很简单就能实现&#xff1a; <a href"tel:13556891235">点击给我打电话</a>2、在H5页面中调起手机发送短信&#xff1a; <a href"sms:18688888888">发短信</a>3、要想实现点击一个按钮我…

React开发(147):开发注意

## 列表查询做法&#xff1a; 以下查询条件值包括&#xff1a;普通查询和高级查询 1. 首先普通查询和高级查询的值是不相互关联的。 2. 点击查询按钮时会重置 pageIndex 为1&#xff0c;否则查询结果有误差 2. 重置不会重置列表 title 的 sort 和 filter&#xff0c;也不会重置…

中国移动话费查询,短信查询,各种免费查询!以后别打10086了

发送YE/CXYE到10086,余额查询 发送CXBX到10086&#xff0c;查询当月套餐剩余短信条数。 发送CXGFX到10086&#xff0c;查询当月飞信GPRS套餐剩余流量。 发送CXGTC到10086&#xff0c;查询当月GPRS套餐剩余流量。 发送CXCCT到10086&#xff0c;查询当月超级畅听套餐剩余流量。 发…