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语言结构体字节对齐的所有内容,创作不易!!
欢迎读者评论提问、点赞、关注!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL从0到1全教程【1】MySQL数据库的基本概念以及MySQL8.0版本的部署

1 MySQL数据库的相关概念 1.1 数据库中的专业术语 1.1.1 数据库 (DB) 数据库是指:保存有组织的数据的容器(通常是一个文数据库 (database)件或一组文件)。 1.1.2 数据库管理系统 (DBMS) 数据库管理系统(DBMS)又称为数据库软件(产品)&#xff0c;用于管理DB中的数据 注意:…

【MySQL】视图,15道常见面试题---含考核思路详细讲解

目录 一 视图 1.1视图是什么 1.2 创建视图 1.3 查看视图(两种) 1.4 修改视图(两种) 1.5 删除视图 二 外连接&内连接&子查询介绍 2.1 外连接 2.2 内连接 2.3 子查询 三 外连接&内连接&子查询案例 3.1 了解表结构与数据 3.2 15道常见面试题 四 思…

道路拆除的题解

目录 原题描述&#xff1a; 题目描述 输入格式 输出格式 样例 #1 样例输入 #1 样例输出 #1 样例 #2 样例输入 #2 样例输出 #2 提示 题目大意&#xff1a; 主要思路&#xff1a; 至于dis怎么求&#xff1f; 代码code&#xff1a; 原题描述&#xff1a; 题目描述 …

盖子的c++小课堂——第二十四讲:差分数组

前言 嗨嗨嗨&#xff0c;这里是盖子的小课堂哟&#xff0c;这次更新主要是因为快放假了&#xff0c;时间多了&#xff0c;好嘞&#xff0c;废话不多说&#xff0c;点赞评论拿来吧你~ 差分数组 一维差分数组 假设给你一个数组 nums &#xff0c;先对区间 [a,b] 中每个元素加…

Android Canvas图层saveLayer剪切clipPath原图addCircle绘制对应圆形区域并放大,Kotlin(3)

Android Canvas图层saveLayer剪切clipPath原图addCircle绘制对应圆形区域并放大&#xff0c;Kotlin&#xff08;3&#xff09; 在文章2 Android Canvas图层saveLayer剪切clipPath原图addCircle绘制对应圆形区域&#xff0c;Kotlin&#xff08;2&#xff09;-CSDN博客 的基础上&…

LightGBM原理和调参

背景知识 LightGBM(Light Gradient Boosting Machine)是一个实现GBDT算法的框架&#xff0c;具有支持高效率的并行训练、更快的训练速度、更低的内存消耗、更好的准确率、支持分布式可以处理海量数据等优点。 普通的GBDT算法不支持用mini-batch的方式训练&#xff0c;在每一次…

【博士每天一篇文-算法】Graph Structure of Neural Networks

阅读时间&#xff1a;2023-11-12 1 介绍 年份&#xff1a;2020 作者&#xff1a;尤家轩 斯坦福大学 期刊&#xff1a; International Conference on Machine Learning. 引用量&#xff1a;130 论文探讨了神经网络的图结构与其预测性能之间的关系。作者提出了一种新的基于图的…

如何在simulink中怎么获取足端轨迹代码解释?

在使用Java代码框架统计用户获取足端轨迹时&#xff0c;我们可以使用Simulink的外部接口功能和Java的网络编程来实现。 我们需要在Simulink中配置外部接口以便与Java进行通信。可以使用Simulink中的TCP/IP或UDP模块来实现网络通信。假设我们选择TCP/IP模块。 足端轨迹是机器人运…

kubernetes(k8s)集群常用指令

基础控制指令 # 查看对应资源: 状态 $ kubectl get <SOURCE_NAME> -n <NAMESPACE> -o wide 查看默认命名空间的pod [rootk8s-master ~]# kubectl get pod NAME READY STATUS RESTARTS AGE nginx 1/1 Running 0 3h53m查看所有pod [roo…

超过80%大厂都在用,Jetpack Compose现代Android界面开发的未来

超过80%大厂都在用&#xff0c;Jetpack Compose现代Android界面开发的未来 1. 引言 Jetpack Compose是一款用于构建Android界面的现代化工具包。目前该框架已经相对成熟&#xff0c;大厂包括Google、字节、阿里等大厂都在使用。根据反馈&#xff0c;普遍认为开发效率提高了很…

Linux最常用的几个系统管理命令

文章目录 Linux最常用的几个系统管理命令查看网络信息的原初 ifconfig默认无参数使用-s显示短列表配置IP地址修改MTU启动关闭网卡 显示进程状态 ps语法几个实例默认情况显示所有进程查找特定进程信息 任务管理器的 top常规使用显示完整命令设置信息更新次数设置信息更新时间显示…

智谱AI大模型ChatGLM3-6B更新,快來部署体验

ChatGLM3 是智谱AI和清华大学 KEG 实验室联合发布的新一代对话预训练模型。ChatGLM3-6B 是 ChatGLM3 系列中的开源模型&#xff0c;在保留了前两代模型对话流畅、部署门槛低等众多优秀特性的基础上&#xff0c;ChatGLM3-6B 引入了如下特性&#xff1a; 1.更强大的基础模型&…

FlinkAPI开发之数据合流

案例用到的测试数据请参考文章&#xff1a; Flink自定义Source模拟数据流 原文链接&#xff1a;https://blog.csdn.net/m0_52606060/article/details/135436048 概述 在实际应用中&#xff0c;我们经常会遇到来源不同的多条流&#xff0c;需要将它们的数据进行联合处理。所以…

JMeter 批量接口测试

一、背景 最近在进行某中台的接口测试准备&#xff0c;发现接口数量非常多&#xff0c;有6、70个&#xff0c;而且每个接口都有大量的参数并且需要进行各种参数验证来测试接口是否能够正确返回响应值。想了几种方案后&#xff0c;决定尝试使用JMeter的csv读取来实现批量的接口…

【Docker项目实战】使用Docker部署nullboard任务管理工具

【Docker项目实战】使用Docker部署nullboard任务管理工具 一、nullboard介绍1.1 nullboard简介1.2 任务看板工具介绍 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍2.3 注意事项 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四…

export default 和exprot

1.默认导入和默认导出 语法: export default {需要输出的内容} 接收: import 成员变量的名字 from 文件夹的路径 案例&#xff1a; a.mjs文件夹下默认导出 export default{a:10,b:20,show(){console.log(123);} } 在b.mjs文件中用成员变量进行接收 import AA from &q…

【昕宝爸爸定制】如何将集合变成线程安全的?

如何将集合变成线程安全的? ✅典型解析&#x1f7e2;拓展知识仓☑️Java中都有哪些线程安全的集合&#xff1f;&#x1f7e0;线程安全集合类的优缺点是什么&#x1f7e1;如何选择合适的线程安全集合类☑️如何解决线程安全集合类并发冲突问题✔️乐观锁实现方式 (具体步骤)。✅…

城堡世界源码

随着数字技术的飞速发展和人们对于娱乐需求的不断提升&#xff0c;城堡世界源码开发逐渐成为了新的热门话题。城堡世界是一个集潮流、艺术、科技于一体的数字娱乐新领域&#xff0c;通过将虚拟现实、增强现实等技术融入传统玩具设计中&#xff0c;为玩家们带来了全新的互动体验…

建站为什么需要服务器?(Web服务器与计算机对比)

​  在部署网站时&#xff0c;底层基础设施在确保最佳性能、可靠性和可扩展性方面发挥着至关重要的作用。虽然大多数人都熟悉个人计算机 (PC) 作为日常工作和个人任务的设备&#xff0c;但 PC 和 Web 服务器之间存在显著差异。在这篇文章中&#xff0c;我们将讨论这些差异是什…

拼多多API的未来:无限可能性和创新空间

拼多多&#xff0c;作为中国电商市场的巨头之一&#xff0c;自成立以来一直保持着高速的发展态势。其API的开放为开发者提供了无限的可能性和创新空间&#xff0c;使得更多的商业逻辑和功能得以实现。本文将深入探讨拼多多API的未来发展&#xff0c;以及它所具备的无限可能性和…