字节对齐的规则总结

2023-05-16

一、什么是字节对齐,为什么要对齐?

现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。

二、请看下面的结构:

struct MyStruct{
  double dda1;
  char dda;
  int type
};

对结构MyStruct采用sizeof会出现什么结果呢?sizeof(MyStruct)为多少呢?也许你会这样求:
sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13
但是当在VC中测试上面结构的大小时,你会发现sizeof(MyStruct)为16。你知道为什么在VC中会得出这样一个结果吗?
其实,这是VC对变量存储的一个特殊处理。为了提高CPU的存储速度,VC对一些变量的起始地址做了“对齐”处理。在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式(vc6.0,32位系统)。

类型对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
Char 偏移量必须为sizeof(char)即1的倍数
int 偏移量必须为sizeof(int)即4的倍数
float 偏移量必须为sizeof(float)即4的倍数
double 偏移量必须为sizeof(double)即8的倍数
Short 偏移量必须为sizeof(short)即2的倍数

各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。

下面用前面的例子来说明VC到底怎么样来存放结构的。

struct MyStruct{
  double dda1;
  char dda;
  int type
};

为上面的结构分配空间的时候,VC根据成员变量出现的顺序和对齐方式,先为第一个成员dda1分配空间,其起始地址跟结构的起始地址相同(刚好偏移量0刚好为sizeof(double)的倍数),该成员变量占用sizeof(double)=8个字节;接下来为第二个成员dda分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为8,是sizeof(char)的倍数,所以把dda存放在偏移量为8的地方满足对齐方式,该成员变量占用sizeof(char)=1个字节;接下来为第三个成员type分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为9,不是sizeof(int)=4的倍数,为了满足对齐方式对偏移量的约束问题,VC自动填充3个字节(这三个字节没有放什么东西),这时下一个可以分配的地址对于结构的起始地址的偏移量为12,刚好是sizeof(int)=4的倍数,所以把type存放在偏移量为12的地方,该成员变量占用sizeof(int)=4个字节;这时整个结构的成员变量已经都分配了空间,总的占用的空间大小为:8+1+3+4=16,刚好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,所以没有空缺的字节需要填充。所以整个结构的大小为:sizeof(MyStruct)=8+1+ 3+4=16,其中有3个字节是VC自动填充的,没有放任何有意义的东西。

下面再举个例子,交换一下上面的MyStruct的成员变量的位置,使它变成下面的情况:

struct MyStruct{
  char dda;
  double dda1;
  int type
};

这个结构占用的空间为多大呢?在VC6.0环境下,可以得到sizeof(MyStruc)为24。结合上面提到的分配空间的一些原则,分析下VC怎么样为上面的结构分配空间的。(简单说明):

struct MyStruct
{
  char dda; //偏移量为0,满足对齐方式,dda占用1个字节;
  double dda1;//下一个可用的地址的偏移量为1,不是sizeof(double)=8的倍数,需要补足7个字节才能使偏移量变为8(满足对齐方式),因此VC自动填充7个字节,dda1存放在偏移量为8的地址上,它占用8个字节。
  int type; //下一个可用的地址的偏移量为16,是sizeof(int)=4的倍数,满足int的对齐方式,所以不需要VC自动填充,type存放在偏移量为16的地址上,它占用4个字节。
};
//所有成员变量都分配了空间,空间总的大小为1+7+8+4=20,不是结构的节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,所以需要填充4个字节,以满足结构的大小为sizeof(double)=8的倍数。所以该结构总的大小为:sizeof(MyStruc)为1+7+8+4+4=24。其中总的有7+4=11个字节是VC自动填充的,没有放任何有意义的东西。

VC对结构的存储的特殊处理确实提高CPU存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。

VC 中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数,否则必须为n的倍数。下面举例说明其用法。

#pragma
pack(push) //保存对齐状态
#pragma
pack(4)//设定为4字节对齐
struct test
{
char m1;
double m4;
int m3;
};
#pragma
pack(pop)//恢复对齐状态

以上结构的大小为16,下面分析其存储情况,首先为m1分配空间,其偏移量为0,满足我们自己设定的对齐方式(4字节对齐),m1占用1个字节。接着开始为m4分配空间,这时其偏移量为1,需要补足3个字节,这样使偏移量满足为n=4的倍数(因为sizeof(double)大于n),m4占用8个字节。接着为m3分配空间,这时其偏移量为12,满足为4的倍数,m3占用4个字节。这时已经为所有成员变量分配了空间,共分配了16个字节,满足为n的倍数。如果把上面的#pragma pack(4)改为#pragma pack(16),那么我们可以得到结构的大小为24。(请读者自己分析)

三、再看下面这个例子

#pragma pack(8)
struct S1{
char a;
float b;
};
struct S2 {
char c;
struct S1 d;
double e;
};
#pragma pack()
sizeof(S2)结果为24.

成员对齐有一个重要的条件,即每个成员分别对齐.即每个成员按自己的方式对齐.
也就是说上面虽然指定了按8字节对齐,但并不是所有的成员都是以8字节对齐.其对齐的规则是,每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是8字节)中较小的一个对齐.并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节.

S1中,成员a是1字节默认按1字节对齐,指定对齐参数为8,这两个值中取1,a按1字节对齐;成员b是4个字节,默认是按4字节对齐,这时就按4字节对齐,所以sizeof(S1)应该为8;

S2 中,c和S1中的a一样,按1字节对齐,而d 是个结构,它是8个字节,它按什么对齐呢?对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个,S1的就是4.所以,成员d就是按4字节对齐.成员e是8个字节,它是默认按8字节对齐,和指定的一样,所以它对到8字节的边界上,这时,已经使用了12个字节了,所以又添加了4个字节的空,从第16个字节开始放置成员e.这时,长度为24,已经可以被8(成员e按8字节对齐)整除.这样,一共使用了24个字节.

a b
S1的内存布局:1***,1111,
c S1.a S1.b d
S2的内存布局:1***,11**,1111,****11111111

**这里有三点很重要:
1.每个成员分别按自己的方式对齐,并能最小化长度。
2.复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。
3.对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐。**

Win32平台下的微软编译器(cl.exe for 80×86)的对齐策略:

1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除

备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。

2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding)

备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。

3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)

备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

字节对齐的规则总结 的相关文章

  • 玩转Jetson Nano(三):安装Pytorch GPU版

    玩转Jetson Nano xff08 三 xff09 xff1a 安装Pytorch GPU版 前言安装Pytorch GPU版查看CUDA版本号下载Pytorch对应的wheel文件测试是否安装成功常见问题OSError libmpi
  • 可随身携带的工业无人机?! ZR-M66六旋翼多镜头倾斜摄影无人飞行系统

    更多无人机测绘资讯 xff0c 关注 三维前沿 ZR M66六旋翼多镜头倾斜摄影无人飞行系统 xff0c 集合了领先于行业内的多种设计方式 xff0c 具备模块化设计 高效作业 安全可靠 直观作业管理四大核心优势 xff0c 摆脱传统测绘作
  • PLC寻址应注意的问题

    L MD100 LAR1 与 L MD100 LAR1 有什么区别 xff1f 当将MD100以这种 MD100 形式表示时 xff0c 你既要在对MD100赋值时考虑到所赋的值是否符合存储器间接寻址双字指针的规范 xff0c 又要在使用这
  • STL简介以及STL中的容器

    我认为STL 标准模板库 就是对常见数据结构和算法的再封装 xff0c 以便开发者能更加灵活的使用数据结构和常用算法 通常认为 xff0c STL由容器 算法 迭代器 函数对象 适配器 内存分配器 这 6 部分构成 容器 简单理解容器 xf
  • 奇偶校验原理及C实现

    奇偶校验 xff0c 是通过计算数据流中比特位1的个数向原数据流后补充1bit的 0 或 1 xff0c 来检查数据流是否正确的方法 奇校验 xff08 odd parity xff09 xff1a 如果字符数据位中 1 的数目为偶数 xf
  • PHP curl发送 post GET 请求 携带请求头 并接收参数 上传\下载文件或图片

    发送post 请求 64 param sUrl 请求地址 64 param aData 请求参数 64 return bool string function http post sUrl aData ch 61 curl init cur
  • C++ 类对象的初始化顺序

    C 43 43 构造函数调用顺序 1 创建派生类的对象 xff0c 基类的构造函数优先被调用 xff08 也优先于派生类里的成员类 xff09 xff1b 2 如果类里面有成员类 xff0c 成员类的构造函数优先被调用 xff1b 也优先于
  • requests模块中使用代理proxy发送请求

    1 代理概述 玩爬虫为什么我们不能使用一个固定IP发送请求 你使用一个固定IP发送每秒向对方服务器发送10几个请求 对方认为这样操作不是人干的 就把你IP给封了服务器端的人可以根据你IP很快锁定你 要求你对这种窃取行为赔偿 代理 外链图片转
  • FIFO理解

    小白一枚 以下是我在学习SCI通信时 xff0c 使用到FIFO寄存器对其手册进行学习的一些经验与心得 xff0c 为了防止以后使用时忘记 xff0c 故此记录 仅供本人使用 另 xff1a 如有理解不当之处 xff0c 欢迎指正 xff0
  • 使用CMake导入第三方库

    欢迎关注微信公众号 自动驾驶事务所 获取更多知识 欢迎关注公众号 自动驾驶事务所 xff0c 分享更多以C C 43 43 为计算机语言 xff0c 以自动驾驶为方向的文章 使用CMakeLists txt 中间需要导入第三方的库当我们需要
  • 通过onvif协议接入海康、大华NVR步骤

    通过onvif协议接入海康 大华NVR步骤 https wenku baidu com view 6f1fcf37192e45361066f54b html
  • C语言的二维数组初始化的几种方式介绍(私藏大数组初始化方式)

    C语言的二维数组初始化的几种方式介绍 1 直接赋值2 循环对每个元素赋值3 借用memset memset s初始化为0或 14 96 数组所有元素初始化为相同值 xff08 用于大数组初始化贼方便 xff09 96 1 直接赋值 适合数组
  • 怎么用管理员方式打开压缩包

    今天下载了安卓的源代码 xff0c 解压时 xff0c 报了 34 Cannot create symbolic link xxx 34 34 You may need to run WinRAR as administrator 34 的
  • Android的build-tools的下载方式

    Android的build tools的多种版本的下载 1 方式一 xff1a Android Studio上的配置下载2 官网上直接下载 1 方式一 xff1a Android Studio上的配置下载 通过参数 buildToolsVe
  • Visual Studio上一些Error的解决方案

    近期在迁移一个linux上下项目到windows xff0c 编译时出来一堆error xff0c 挑了一些做记录 目录 1 E1696无法打开源文件 unistd h2 E0020 未定义标识符 34 getcwd 34 3 E0020
  • windows下怎么查看exe是32位还是64位

    xxx exe用记事本或notepad打开 xff0c 找有字符的第二行中 PE 字符串旁边 xff0c 如果是 d xff0c 则为64位 xff1b 如果是 L xff0c 则为32位
  • 无法定位程序输入点 _ZNSt7__cxx1118basic_stringstreamIcSt11char_traitsIcESaIcEEC1Ev于动态链接库

    在执行编译出来的exe时 xff0c 报了 无法定位程序输入点 ZNSt7 cxx1118basic stringstreamIcSt11char traitsIcESaIcEEC1Ev于动态链接库 的异常 出现这个问题时 xff0c 应该
  • 踩了个C++的未定义标识符"cout"的坑

    问题表现 没怎么用过C 43 43 写过完整的项目 xff0c 今天闲来无事 xff0c 便创建个c 43 43 的工程玩玩 xff0c 结果一个简单的打印输出就给卡住了 xff0c 无法打开文件 iostream h xff0c cout
  • Electron-Vue之安装流程

    近期摒弃了熟悉的WPF xff0c 选用了新的一套工具 xff08 Electron Vue xff09 来开发桌面软件 xff08 我是连html都没用过的猿 xff0c no zuo no die xff09 接触新的东西 xff0c
  • vscode的调试配置

    文章目录 vscode的调试配置文件调试配置选项 vscode的调试配置文件 vscode的调试配置存储在 vscode文件夹的launch json文件中 通过以下步骤可以创建一个调试配置 xff1a 切换到调试视图单击create a

随机推荐

  • C/C++实现strcpy和strcat两个功能

    strcpy和strcat是string h头文件中分别实现字符串数组拷贝与拼接功能的函数 xff0c 详细使用相信大家都了解了 xff0c 如果还不了解看看实例 C C 43 43 笔试必须熟悉掌握的头文件系列 xff08 四 xff09
  • C/C++锁机制(boost)的认知和使用

    锁扩充 加锁的必需考虑三个问题 该锁的不锁 xff0c 将会导致各种莫名其妙的错误 xff1b 加锁范围太大 xff0c 虽然能避免逻辑错误 xff0c 但如果锁了不该锁的东西 xff0c 难免会降低程序的效率 xff1b 加锁方式不合适
  • QT之GPS

    http blog sina com cn s blog 7da13b510100xtgr html 前几天发现手里还闲着一块GPS 佳明的15W 也不知道是好的还是坏的呵呵一时兴起我就趁周六日没什么事情 用qt做了一个界面 现在已经调试完
  • 关于tcp/udp网络调试助手错误提示

    最近在学习网络调试助手与虚拟机中的Ubuntu系统通信 xff0c 在使用Ubuntu做服务器端时 xff0c tcp以及udp协议都遇到了问题 1 tcp协议遇到的问题是 xff1a 网络调试助手提示 xff1a 1035未知错误 xff
  • 结构体和结构体链表

    在c语言表针中有多种数据类型 xff0c 他们的应用使变量的应用变得灵活多变 而除了c语言默认的int xff0c float 等类型外 xff0c 我们还可以自己定义一些数据的类型 xff0c 结构体类型便是可以实现数据类型自定义的类型
  • 串口通信UART

    串口基本概念 串口通讯 Serial Communication 是指外设和计算机间 xff0c 通过数据信号线 地线等 xff0c 按位进行传输数据的一种通讯方式 其通讯协议可分层为协议层和物理层 物理层规定通信协议中具有机械 电子功能的
  • 一、Fmcw毫米波雷达原理

    0 概念 FMCW Frequency Modulated Continuous Wave xff0c 即调频连续波 FMCW技术和脉冲雷达技术是两种在高精度雷达测距中使用的技术 其基本原理为发射波为高频连续波 xff0c 其频率随时间按照
  • Makefile和CMake

    Makefile makefile主要规则 xff1a 伪对象 PHONY clean 规则1 main main o gcc main o o main 规则2 main o main c gcc c main c o main o 规则
  • C语言基础——结构体

    结构体的作用 在需要表示一些复杂信息时 xff0c 使用单纯的数据类型很不方便 比如 xff1a 学生信息 xff08 学号 xff0c 姓名 xff0c 班级 xff0c 电话 xff0c 年龄 xff09 xff1b GPIO信息 xf
  • Nginx 通过 header 中的标识进行分发

    Nginx可以根据请求头中自定义的标识将请求分发到不同的服务器 具体来说 xff0c 可以使用map指令将请求头中的自定义标识映射为不同的后端服务器地址 xff0c 然后使用proxy pass指令将请求转发到对应的后端服务器 以下是一个示
  • DB9接口详解---DB9引脚在 UART,CAN,RS485中的定义

    DB9的公母如下 xff1a 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
  • 超强整理!PCB设计之电流与线宽的关系

    关于pcb线宽和电流的经验公式 xff0c 关系表和软件网上都很多 xff0c 本文把网上的整理了一下 xff0c 旨在给广大工程师在设计PCB板的时候提供方便 以下总结了八种电流与线宽的关系公式 xff0c 表和计算公式 xff0c 虽然
  • nginx 主动健康检查搭建详解(nginx_upstream_check_module)

    版本信息 nginx 1 21 1 下载nginx upstream check module模块 nginx upstream check module master zip wget https codeload github com
  • paddle推理部署(cpu)

    我没按照官方文档去做 xff0c 吐槽一下 xff0c 官方文档有点混乱 一 概述 总结起来 xff0c 就是用c 43 43 示例代码 xff0c 用一个模型做推理 二 示例代码下载 https www paddlepaddle org
  • Vector的用法

    我不知道大家是怎么理解Vector和怎样使用的 xff0c 这篇文章主要是发表我自己对于Vector的看法 xff0c 仅仅属于个人理解 xff0c 如果有什么错误 xff0c 也希望大家指正哈 目录 1 xff1a Vector的概念 2
  • float的表示

    xfeff xfeff 先说一下计算机中二进制的算法 xff1a 整数 整数的二进制算法大家应该很熟悉 xff0c 就是不断的除以2取余数 xff0c 然后将余数倒序排列 比如求9的二进制 xff1a 9 2 61 4 余 1 4 2 61
  • cmake系列(三)

    目录 多个源文件 同一目录 xff0c 多个源文件 多个源文件 同一目录 xff0c 多个源文件 本小节对应的源代码所在目录 xff1a Demo2 上面的例子只有单个源文件 现在假如把 power 函数单独写进一个名为 MathFunct
  • ORACLE 字符串聚合函数 strcat

    create or replace type strcat type as object currentstr varchar2 4000 currentseprator varchar2 8 static function ODCIAgg
  • 无人机器件选择参考

    无人机飞控 xff0c 引脚预留数量 1 xff0c 四路pwm 2 xff0c 无线通信spi 3 xff0c 陀螺仪通信用iic 4 xff0c 串口调试用uart 5 xff0c led灯用普通io 6 xff0c 电量检测和电机堵塞
  • 字节对齐的规则总结

    一 什么是字节对齐 为什么要对齐 现代计算机中内存空间都是按照byte划分的 xff0c 从理论上讲似乎对任何类型的变量的访问可以从任何地址开始 xff0c 但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问 xff0c 这就需要