C语言结构体的字节对齐

C语言结构体的字节对齐

什么是字节对齐

首先来看下面的程序:

#include <stdio.h>typedef struct n1{int a;char b;char c; 
} N_stru1;typedef struct n2{char b;int a;char c;
} N_stru2;int main() {N_stru1 n1;N_stru2 n2;printf("%d\n", sizeof(n1));printf("%d\n", sizeof(n2));return 0;
}

两个输出的结果是什么?

如图:
在这里插入图片描述
明明结构体中的成员类型数量都是一样的,为什么会出现存储他们的结构体大小不一样的情况呢?

原因是这样的,下面是这两个结构体在内存中的存储结构:

n1:

地址0x000x010x020x030x040x050x060x07
内容a---bc//

n2:

地址0x000x010x020x030x040x050x060x070x080x090x100x11
内容b///a---c///

ps:在上面的表格中,“-”表示被使用,“/”表示被空置或者跳过

通过观察表格会发现:n2这个结构体中的字节有很多的空间被浪费了(因为有更多空置的内存),所以可以得出一个结论:

C语言的编译器在给结构体分配空间时按照结构体成员的声明顺序分配,并且其空间遵循内存边界要求最严格的成员的空间大小分配空间

如果你已经明白,那么可以跳过下面这个小板块的内容,如果还是没有,我讲配合上面的例子进行说明:


编译器对于结构体成员分配结论的详细解释

上面的结论有两点:

  • 分配空间时按照结构体成员的声明顺序分配
  • 遵循内存边界要求最严格的成员的空间大小分配空间

第一点,观察上表:
可以发现n1中的成员从地址最低位向最高位依次是:a、b、c
而n2中的成员从地址最低位向最高位依次是:b、a、c

这个顺序和我们在程序中对结构体成员的声明顺序是相同的。

第二点:
在这个例子中,发现n1和n2的大小分别是8和12,他们都是4的倍数。
为什么要说是4的倍数呢?
因为在结构体成员中,所需空间最大的变量就是int类型,也就是4个字节,所以在这个例子中,4就是内存边界要求最严格的成员变量。
那么比4小的1个字节是不能构成分配空间的单位的,所以说,编译器会在不足边界要求的成员的内存分配中给他们填充一些空的空间,以达到最低要求。
所以在上面的例子中:
n1:

地址0x000x010x020x030x040x050x060x07
内容a---bc//

a是int类型,占4个字节,系统直接分配,
b是char类型,占1个字节,不足4字节,系统需要填充三个字节
c也是char类型,占1个字节,不足4字节,但由于前面有空的字节,所以直接放在空字节中,不再分配空间

n2:

地址0x000x010x020x030x040x050x060x070x080x090x100x11
内容b///a---c///

按顺序首先分配b,是char类型,不足4,填充3个字节
然后是a,满足4,不做处理直接分配
最后是c,不足4,前面字节已经被占用,因此再填充3个字节,分配

以上是对于结论的详细描述,不懂的可以在评论区提问。


那回到正题,什么是字节对齐呢?

下面是字节对齐的几个原则:

  • 结构体的自身对齐值是其成员中自身对齐值最大的那个值,或者是指定对齐值(如果有的话)。
  • 结构体的大小必须是其自身对齐值的整数倍,如果不足则补齐。
  • 结构体的每个成员必须放在其自身对齐值的整数倍的地址上,如果不够则空出一些字节。

很多文章没有提到“对齐”这个概念,这里特别做一下解释:
“成员的空间分配 遵循内存边界要求最严格的成员的空间大小 分配空间”
其实就是上文中结论的后半部分

这里不再对64位和32位的系统进行比较,他们都遵循上述规则。

为什么要字节对齐

首先要明确的一点就是:CPU在内存中的取值是怎么样的。

操作系统位数与信息处理

显然,如果你的电脑是64位,那么CPU最大可以一次取64位(64bit换算成字节是8字节)宽的值(在内存中),然后对他们进行处理,这意味着:系统的操作位数越高,电脑运行速度会更快,处理多信息的能力就越强。

CPU读取数据的格式

CPU在访问内存时,通常是以一个字为单位,而一个字的长度就取决于CPU的位数。在64位操作系统中,一个字通常是8字节。那么一次性在内存中可以读取的字节大小就是8个字节,很舒服的是,在x86系统架构中,内存中一个内存块(也叫缓存行)的大小就是8个字节,这也恰恰是编译器在系统中开辟内存的单位,CPU一次性就可以读取这个内存块中的所有内容。

这个和字节对齐有什么关系呢?

现在来想一个问题:假设你正在给内存分配变量空间,现在已经分配到内存块的最后一个字节了(也就是说,这个内存块前面的内容已经被其他变量占用了,8字节的内存块只有1个字节是空闲的),此时你要分配一个4字节整形变量,那么你选择以下哪种分配方式?

  • 从这个内存块最后剩下的1个字节开始分配一部分,然后再把剩下的一部分放在新的内存块中
  • 直接丢弃空余的1字节,直接把这个变量放在新的内存块中。

仔细看过上面的文章的人应该都会选择第二个选项,因为这样可以减少CPU读取数据的次数。

字节对齐的意义
这就是字节对齐的意义,如果你字节对齐,你会发现,在C语言中,无论你怎么访问变量(除非这个变量大于内存块的大小),访问次数永远都是1次!!!不会出现CPU读取两次内存的情况!!!这样一来大大的提高了内存访问的速度

如何用利用系统字节对齐的特性提高自己的编程水平?

很简单,根据上面的例子就可以看出,当在结构体中声明变量的时候,我们应该尽量做到以下几点:

  • 相同的数据类型放在一起
  • 数据类型大的变量放在声明顺序之前
  • 尽量的声明4的整数倍大小的空间(哪怕多声明的变量没有用)

对于最后一点,解释是这样的:

如果你声明了一个三个字节大小的空间(short是2个字节,加上1个char类型是三个字节),那么在字节对齐的影响下,最后一个字节就不能使用了,这块空间没有任何用,你为什么不多开辟一个变量,说不定未来能用上呢?


以上就是C语言结构体字节对齐的所有内容,创作不易!!
欢迎读者评论提问、点赞、关注!!!

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

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

相关文章

AIGC视频生成:Pika1.0快速入门详解

Pika1.0快速入门详解 一、简介二、登录三、参数设置1、改变画面大小&#xff08;Aspect ratio&#xff09;2、改变帧数大小&#xff08;Frames per second&#xff09;3、镜头平移&#xff08;Camera control&#xff09;4、画面运动控制&#xff08;Strength of motion&#x…

c++ 经典服务器开源项目 Tinywebserver学习笔记

learning make me happy---更新中 疑问部分ENGINEInnoDB 存储引擎指定为innoDB的作用的意义&#xff1f; 报错部分fatal error: mysql/mysql.h: No such file or directory&#xff1f;进程结束后还占用大量内存&#xff1f; 知识学习和查漏补缺epoll_create&#xff08;5&…

面试算法110:所有路径

题目 一个有向无环图由n个节点&#xff08;标号从0到n-1&#xff0c;n≥2&#xff09;组成&#xff0c;请找出从节点0到节点n-1的所有路径。图用一个数组graph表示&#xff0c;数组的graph[i]包含所有从节点i能直接到达的节点。例如&#xff0c;输入数组graph为[[1&#xff0c…

机器学习 -- 余弦相似度

场景 我有一个 页面如下&#xff08;随便找的&#xff09;&#xff1a; 我的需求是拿到所有回答的链接&#xff0c; 再或者我在找房子网上&#xff0c;爬到所有的房产信息&#xff0c;我们并不想做过多的处理&#xff0c;我只要告诉程序&#xff0c;请帮我爬一个类似 xxx 相似…

golang并发安全-select

前面说了golang的channel&#xff0c; 今天我们看看golang select 是怎么实现的。 数据结构 type scase struct {c *hchan // chanelem unsafe.Pointer // 数据 } select 非默认的case 中都是处理channel 的 接受和发送&#xff0c;所有scase 结构体中c是用来存储…

秒变办公达人,只因用了这5款在线协同文档app!

在日常工作中&#xff0c;我们不可避免地需要处理各种文档&#xff0c;有时你可能会为如何高效地管理这些文档而感到烦恼&#xff0c;或是不知道如何挑选合适的在线文档工具&#xff1f; 不用担心&#xff01;在这篇文章中&#xff0c;我们将介绍5个好用的在线文档工具App&…

Hive精选10道面试题

1.Hive内部表和外部表的区别&#xff1f; 内部表的数据由Hive管理&#xff0c;外部表的数据不由Hive管理。 在Hive中删除内部表后&#xff0c;不仅会删除元数据还会删除存储数据&#xff0c; 在Hive中删除外部表后&#xff0c;只会删除元数据但不会删除存储数据。 内部表一旦…

图像分类任务的可视化脚本,生成类别json字典文件

1. 前言 之前的图像分类任务可视化&#xff0c;都是在train脚本里&#xff0c; 用torch中dataloader将图片和类别加载&#xff0c;然后利用matplotlib库进行可视化。 如这篇文章中&#xff1a;CNN 卷积神经网络对染色血液细胞分类(blood-cells) 在分类任务中&#xff0c;必定…

SSM+mysql电影推荐系统-计算机毕业设计源码030873

目 录 摘 要 Abstract 第1章 前 言 1.1 研究背景 1.2 研究现状 1.3 系统开发目标 第2章 技术与原理 2.1 开发技术 2 2.2 ssm框架介绍 2 2.3 MySQL数据库 2 2.4 B/S结构 2 第3章 需求分析 3.1 需求分析 3.2 系统可行性分析 3.3 项目设计目标与原则 3.4…

软件测试工程师经典面试题总结

一、接口测试如何设计测试用例&#xff1f; 首先&#xff0c;接口测试用例与其他测试用例是一样的&#xff0c;都是为了证明程序存在错误&#xff0c;其出发点相同&#xff1b;接口测试用例的对象是接口&#xff0c;需要验证各个系统及组件间的接口&#xff1b;其三是接口测试的…

2019年认证杯SPSSPRO杯数学建模B题(第一阶段)外星语词典全过程文档及程序

2019年认证杯SPSSPRO杯数学建模 基于方差分布的方法对未知语言文本中重复片段的自动搜索问题的研究 B题 外星语词典 原题再现&#xff1a; 我们发现了一种未知的语言&#xff0c;现只知道其文字是以 20 个字母构成的。我们已经获取了许多段由该语言写成的文本&#xff0c;但…

【Vue2】一个数组按时间分割为【今年】和【往年】俩个数组

一. 需求 后端返回一个数组&#xff0c;前端按时间维度将该数组的分割为【今年】和【往年】俩个数组后端返回的数组格式如下 timeList:[{id:1,billTime:"2024-01-10",createTime:"2024-01-10 00:00:00",status:0},{id:2,billTime:"2022-05-25"…