C语言字节对齐规则总结

C语言字节对齐规则总结原始链接源自 nbsp https www cnblogs com clover toeic p 3853132 html nbsp 从上面博客中学习总结得到下面的文章 nbsp nbsp 不同硬件平台 对存储空间的处理不一样 比如不能放奇数地址 不能任意存放等 为了适应不同的架构 在 C 语言层面上 就可以执行对齐从而独立于硬件平台 此外 是由于对内存的存取效率问题 如果存放的地址不对齐 取一个 4 字节的数据 可能会需

原始链接源自 https://www.cnblogs.com/clover-toeic/p/3853132.html , 从上面博客中学习总结得到下面的文章。

    不同硬件平台,对存储空间的处理不一样,比如不能放奇数地址,不能任意存放等,为了适应不同的架构,在C语言层面上,就可以执行对齐从而独立于硬件平台。 此外,是由于对内存的存取效率问题,如果存放的地址不对齐,取一个4字节的数据,可能会需要两个时钟信号才能取完。为了CPU能够对数据进行快速的访问,也要求数据的起始地址具有对齐特性。 比如4字节数据的起始地址应该在4字节的边界上,也就是数据存放的起始地址应该被4整除。这就是为什么要字节对齐, 和什么是字节对齐。

 

    对齐的方式,又区分 结构体对齐、 栈内存对齐、位域对齐, 位域本质上是结构体。

    对于Intel X86平台,每次分配内存应该是从4的整数倍地址开始分配,无论是对结构体变量还是简单类型的变量。

一、结构体对齐
    编译器为结构体的每个成员按照其自然边界(alignment)分配空间。各成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。

    对齐规则:

 1) 数据类型自身的对齐值:char型数据自身对齐值为1字节,short型数据为2字节,int/float型为4字节,double型为8字节。

 2) 结构体的自身对齐值:其成员中自身对齐值最大的那个值

 3) 指定对齐值:#pragma pack (value)时的指定对齐值value。默认是4。

 4) 数据成员、结构体的有效对齐值:自身对齐值和指定对齐值中较小者,即有效对齐值=min{自身对齐值,当前指定的pack值}。

    使用pragma指定对齐值,其实是指定了数据结构的最大对齐值, 如果本身的对齐值,并不超过设定的值,还是会按照自身的对齐值来。

    有效对齐值N是最终用来决定数据存放地址方式的值。有效对齐N表示“对齐在N上”,即该数据的“存放起始地址%N=0”。结构体的成员变量要对齐存放,结构体本身也要根据自身的有效对齐值圆整(即结构体成员变量占用总长度为结构体有效对齐值的整数倍)。

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

    sizeof(A) = 8,  sizeof(B) = 12;

    A中,最初的int是4字节对齐,char是1字节对齐,所以前5个字节不需要填充,后面short长度为2个字节,和2对齐,所以char后面补一个字节,总共是8个字节。整个数组的有效对齐值是成员的最大值4, 8个字节是4的整数倍, 所以结构体总长度为8。

    B中,最初char是1字节,随后int长度是4字节,和4字节对齐,char后面补3个字节,最后short是第9和10字节,也是对齐的。整个结构体的有效对齐值是4,结构体长度需要是4的整数倍,所以short后面还需要补2个字节,总长度为12。

    之所以编译器在后面补充2个字节,是为了实现结构数组的存取效率。试想如果定义一个结构B的数组,那么第一个结构起始地址是0没有问题,但是第二个结构呢?按照数组的定义,数组中所有元素都紧挨着。如果我们不把结构体大小补充为4的整数倍,那么下一个结构的起始地址将是0x0000A,这显然不能满足结构的地址对齐。因此要把结构体补充成有效对齐大小的整数倍。

更改对齐方式:

   在编码时,可用#pragma pack动态修改对齐值。自定义对齐值后要用#pragma pack()来还原,否则会对后面的结构造成影响。

#pragma pack(2) //指定按2字节对齐 struct C{ char b; int a; short c; }; #pragma pack() //取消指定对齐,恢复缺省对齐

    char自身对齐值是1,指定对齐值是2,所以有效对齐值是1。 int自身对齐值是4,指定对齐值是2,所有有效对齐值是2,放在2的倍数的地址空间上,char后面只会补1个字节。所以到这里长度是6,后面short两个字节,有效对齐值也是2,结构体总长度是8。

    需要注意,pragma pack指定的对齐值,是数据类型的最大对齐值,可以小,但是不能大。

    因为对齐,会产生的问题:

    1,数据类型强转可能会因为对齐,出错。 short类型应该放在2的倍数的地址上,但是这里p1却指向了奇数地址。需要注意。

int main(void){ unsigned int i = 0x; unsigned char *p = (unsigned char *)&i; *p = 0x00; unsigned short *p1 = (unsigned short *)(p+1); *p1 = 0x0000; return 0; }

    2,不同的处理器之间传递数据时,因为两个处理器可能采用的填充方式不一致,会导致数据出错。这时,可以在定义数据结构时,自己把需要填充的部分用char类型的数据填上,这样就不会不一致了。或者使用pragma pack 1, 让数据都按照1字节对齐。

二、栈内存对齐

在VC/C++中,栈的对齐方式不受结构体成员对齐选项的影响。总是保持对齐且对齐在4字节边界上。(直接看博客原文吧,他里面说的char和short没有凑到4个字节,我认为是已经凑到一起了)。

三、位域的对齐方式

    有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1两种状态,用一位二进位即可。为了节省存储空间和处理简便,C语言提供了一种数据结构,称为“位域”或“位段”。

    1,为了节省内存,对于大量的结构体数组来讲。 2,需要访问字节内的bit成员。两种情况会使用位域。

    位域成员,除了指定所占用的bit位外,还有一个类型。位域成员不能单独被取sizeof值。下面主要讨论含有位域的结构体的sizeof。

其对齐规则大致为:

     1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;

     2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

     3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++和GCC采取压缩方式;

     4) 如果位域字段之间穿插着非位域字段,则不进行压缩;

     5) 整个结构体的总大小为最宽基本类型成员大小的整数倍,而位域则按照其最宽类型字节数对齐。

 

     位域可以无位域名,只用作填充或调整位置,占位大小取决于该类型。例如,char :0表示整个位域向后推一个字节,即该无名位域后的下一个位域从下一个字节开始存放,同理short :0和int :0分别表示整个位域向后推两个和四个字节。

     当空位域的长度为具体数值N时(如int :2),该变量仅用来占位N位。

    原文还有位域的例子和字节大小端的例子,在这里就不写了。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/217081.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 上午10:23
下一篇 2026年3月18日 上午10:24


相关推荐

  • 【小结】idea全局搜索快捷键

    【小结】idea全局搜索快捷键1 shift shift 双击可以搜索任何东西 类 资源 配置项 方法等 还能搜索路径 几乎这一个快捷键 全局搜索就足够了 2 Ctrl F 在当前类中 页中进行查找相关方法等 3 Ctrl Shift N 按 文件名 搜索文件过滤的结果都是文件 4 Ctrl H 查看类的继承关系选中某个类 然后 Ctrl H 以展开的形式展现出来 5 Alt F7 查看类在哪儿被使用 OK 了

    2026年3月26日
    37
  • 不联网,ubuntu下安装gcc

    不联网,ubuntu下安装gcc1.下载 在GCC网站上(http://gcc.gnu.org/)或者通过网上搜索可以查找到下载资源。目前GCC的最新版本为3.4.0。可供下载的文件一般有两种形式:gcc-3.4.0.tar.gz和gcc-3.4.0.tar.bz2,只是压缩格式不一样,内容完全一致,下载其中一种即可。 2.解压缩 根据压缩格式,选择下面相应的一种方式解包(以下的“%”表示命令行提示符

    2022年7月24日
    28
  • ldd命令 ubuntu_Linux ldd 命令 command not found ldd 命令详解 ldd 命令未找到 ldd 命令安装 – CommandNotFound ⚡️ 坑否…[通俗易懂]

    ldd命令 ubuntu_Linux ldd 命令 command not found ldd 命令详解 ldd 命令未找到 ldd 命令安装 – CommandNotFound ⚡️ 坑否…[通俗易懂]显示行号|选择喜欢的代码风格默认GitHubDuneLakeSidePlateauVibrantBlueEightiesTranquilldd命令打印程序和库的共享库依赖项。注意:ldd不是一个可执行程序,而只是一个Shell脚本。ldd命令安装:-bash:ldd:commandnotfound#Debianapt-getinstalllibc-bin#Ubuntuapt-…

    2022年6月2日
    146
  • idea中选中一行的快捷键_idea撤销快捷键恢复

    idea中选中一行的快捷键_idea撤销快捷键恢复之前前端开发一直使用VSCode,常用快捷键删除一行或者当前选中的几行代码,使用idea的时候发现快捷键并不相同,查看发现idea的快捷是:Ctrl+Y,比手动删除代码方便很多。通过File->Setttings->Keymap可以查看已经设置好的快捷键:…

    2025年9月27日
    4
  • Docker 部署 OpenClaw 注意事项

    Docker 部署 OpenClaw 注意事项

    2026年3月13日
    1
  • DHCP协议详解

    DHCP协议详解文章目录什么是DHCPDHCP协议DHCP报文种类DHCP报文格式DHCP工作流程IP地址分配方式租约表工作流程服务器处理流程什么是DHCPDHCP(DynamicHostConfigurationProtocol,动态主机配置协议),前身是BOOTP协议,是一个局域网的网络协议,使用UDP协议工作,统一使用两个IANA分配的端口:67(服务器端),68(客户端)。DHCP通常被用于局…

    2022年5月10日
    59

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号