【C语言】结构体内存布局解析——字节对齐

🦄个人主页:小米里的大麦-CSDN博客

🎏所属专栏:https://blog.csdn.net/huangcancan666/category_12718530.html

🎁代码托管:黄灿灿 (huang-cancan-xbc) - Gitee.com

⚙️操作环境:Visual Studio 2022

目录

一、引言

二、什么是字节对齐?

 三、字节对齐规则

1. 成员对齐规则:

2. 结构体整体对齐规则:

3. 编译器依赖性:

小结

四、示例分析

内存布局

示例一:简单结构体

示例二:复杂对齐规则

示例三:自定义对齐方式

思考:(自行尝试一下吧)

五、如何自定义对齐方式

使用#pragma pack

 使用__attribute__((packed))

六、总结

共勉


一、引言

在C语言中,结构体是一种用户定义的数据类型,一种非常有用的数据类型,它允许程序员将不同类型的数据组织在一起。结构体的大小并不是简单地等于所有成员变量大小之和,因为编译器会对结构体中的数据进行字节对齐(byte alignment),以优化访问速度。理解结构体在内存中的布局对于编写高效且可维护的代码至关重要。本文将探讨结构体内存布局的基本原理。

二、什么是字节对齐?

字节对齐是指数据在内存中的存储方式,以提高内存访问效率。大多数现代计算机系统在内存访问时,要求数据地址满足特定的对齐条件,否则可能会导致访问效率下降,甚至是硬件异常。

具体来说,数据的对齐要求是由其数据类型决定的。例如,4字节的整型变量通常要求其地址是4的倍数。结构体中的每个成员也必须满足其对齐要求。

对齐规则通常由编译器和处理器架构共同决定。常见的规则包括:

  • 自然对齐:数据类型应该按照它的自然对齐要求放置。例如,一个int类型(假设为4字节)应该放在4字节对齐的位置上。
  • 最大对齐:结构体或联合中的所有成员都将按照最大成员的对齐要求进行对齐。
  • 固定对齐:有时编译器会强制所有成员按某个固定的对齐值进行对齐。

 三、字节对齐规则

1. 成员对齐规则

  • 每个成员会根据其类型自动对齐到一个特定的字节边界。例如,char 类型通常不需要对齐,而 int 可能需要 4 字节对齐。
  • 如果一个成员的偏移量不是其大小的倍数,则会在前面填充空字节以满足对齐要求。也就是浪费空间(以空间换时间)
  • 内存地址是从0开始递增的,因此在内存中,每个字节都有一个唯一的地址,这些地址是从0开始编号的。(这非常重要!!!)

2. 结构体整体对齐规则

  • 结构体自身也有一个对齐要求,通常是最大成员的对齐要求。这意味着整个结构体的大小必须是这个对齐值的倍数。
  • 第一个成员在与结构体变量偏移量为0的地址处。
  • 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
  • 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
  • VS中默认的值为8
  • 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  • 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

3. 编译器依赖性

  • 不同的编译器可能有不同的默认对齐规则,这些规则可以通过预处理器宏或命令行选项来更改。(以VS为例,通常vs是以8字节对齐)

4. 小结

  • 成员的排列顺序:结构体成员按照声明的顺序依次存放。
  • 对齐方式:每个成员按照自身类型的对齐要求对齐。
  • 地址编号:内存地址从0开始编号。
  • 结构体的总大小:结构体的总大小必须是其最宽基本类型成员大小的倍数(即结构体的对齐方式)。

四、示例分析

下面通过几个例子来具体说明结构体大小的计算过程。

首先,让我们定义一个简单的结构体S,它包含了三个成员:一个整型变量a、一个字符数组c以及一个双精度浮点数d

struct S {int a;char c[5];double d;
};
这个结构体中包含的不同数据类型需要不同数量的字节来存储:int a: 假设在32位系统上,int通常占用4个字节。
char c[5]: 每个char占用1个字节,加上数组长度为5,因此占用5个字节。
double d: 在大多数系统上,double占用8个字节。

内存布局

当我们在程序中声明一个S类型的变量时,例如:

struct S s;

编译器会为这个结构体分配连续的内存空间,并按照成员出现的顺序依次存放它们。

  • 整型变量a:占据前4个字节。
  • 字符数组c:紧接着a后面,占据接下来的5个字节。
  • 双精度浮点数d:位于字符数组之后,占据了剩余的8个字节。

这意味着整个结构体S在内存中所占的空间为4 + 5 + 8 = 17个字节。(以8字节对齐,从0开始,最后的一个字节处于 ‘地址16’ 的位置,16刚好是8的倍数,不用增加,大小刚刚好)

请注意,由于不同的编译器可能有不同的内存对齐策略,因此实际的内存布局可能会有所不同。一些编译器会对结构体成员进行填充,以确保某些类型的对齐要求被满足。这种对齐可以帮助提高数据访问的速度,但也可能导致结构体的实际大小大于简单计算得出的大小。 

示例一:简单结构体

struct Example1 {char a;int b;short c;
};
我们逐一分析这个结构体的内存布局:char a,大小为1字节,对齐要求为1字节。
int b,大小为4字节,对齐要求为4字节。为了满足对齐要求,在char a之后需要3字节的填充。
short c,大小为2字节,对齐要求为2字节。int b之后无需填充。

内存布局如下:

| a(1) | 填充(3) | b(4) | c(2) | 填充(2) |

结构体的总大小应是最大对齐要求的倍数,这里是4的倍数,所以最终大小为12字节。

示意图(这样表示只是便于观察,其实并不准确,大概知道内存中是怎样存储即可!!)

| a | - | - | - | b | b | b | b | c | c | - | - |    ————存储0   1   2   3   4   5   6   7   8   9  10   11     ————地址编号

示例二:复杂对齐规则

struct Example2 {double a;char b;int c;
};
内存布局分析:double a,大小为8字节,对齐要求为8字节。
char b,大小为1字节,对齐要求为1字节。double a之后需要7字节的填充。
int c,大小为4字节,对齐要求为4字节。char b之后需要3字节的填充。

内存布局如下:

| a(8) | b(1) | 填充(7) | c(4) | 填充(4) |

结构体的总大小应是最大对齐要求的倍数,这里是8的倍数,所以最终大小为24字节。

示意图如下:

| a | a | a | a | a | a | a | a | b | - | - | - | - | - | - | - | c | c | c | c | - | - | - | - |

示例三:自定义对齐方式

#pragma pack(1)
struct Example3 {char a;int b;short c;
};
#pragma pack()

通过设置#pragma pack(1),我们取消了默认的对齐要求,结构体大小变为:

| a(1) | b(4) | c(2) |

总大小为7字节。 

 示意图如下:

| a | b | b | b | b | c | c |

思考:(自行尝试一下吧)

这里是原代码,尝试一下吧~
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>struct S1
{char c1;int i;char c2;
}S1;
struct S2
{char c1;char c2;int i;
}S2;int main()
{printf("%d\n", sizeof(S1));printf("%d\n", sizeof(S2));return 0;
}

五、如何自定义对齐方式

在某些情况下,我们可以使用#pragma pack指令或__attribute__((packed))来改变默认的对齐方式。

使用#pragma pack

#pragma pack(1)
struct Packed {char a;int b;short c;
};
#pragma pack()

通过设置#pragma pack(1),我们取消了默认的对齐要求,结构体大小变为:

| a(1) | b(4) | c(2) |

总大小为7字节。

 使用__attribute__((packed))

struct Packed {char a;int b;short c;
} __attribute__((packed));

#pragma pack(1)效果相同,总大小也是7字节。

六、总结

理解结构体的内存对齐规则对于优化内存使用和提高程序性能非常重要。以下是一些关键点:

  • 结构体成员按声明顺序排列,满足自身对齐要求。
  • 结构体总大小是其最大对齐要求的倍数。
  • 可以使用#pragma pack__attribute__((packed))自定义对齐方式。
  • 地址编号:内存地址从0开始编号。
  • 对齐规则:数据类型应该按照其大小的倍数对齐。
  • 填充:为了满足对齐要求,编译器可能会在结构体中插入空隙(填充)。

通过这些规则和技巧,我们可以更好地设计和使用C语言中的结构体。希望本文对您理解结构体大小计算和字节对齐有所帮助。

共勉

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

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

相关文章

使用Python绘制雷达图的简单示例

雷达图&#xff08;Radar Chart&#xff09;也被称为蜘蛛网图、星形图或极坐标图&#xff0c;是一种用于显示多变量数据的图形方法。它以一个中心点为起点&#xff0c;从中心点向外延伸出多条射线&#xff0c;每条射线代表一个特定的变量或指标。每条射线上的点或线段表示该变量…

【基础篇】MySQL数据库详解:基础知识详解

一、SQL分类 1.DDL2.DML3.DQL4.DCL二、函数 1.字符串函数2.数值函数3.日期函数4.流程函数三、约束 1.概述2.约束演示3.外键约束四、多表查询 1.多表关系2.多表查询表述3.内连接4.外连接5.自连接6.子查询五、事务 1.事务简介2.事务操作3.事务四大特性4.并发事务问题5.事务隔离级…

Git的一些简单使用

下列内容适用于git初学者&#xff0c;从创建本地git仓库到提交的一个基本过程1. 1.创建git仓库 在想创建git仓库的路径下打开git bash&#xff0c;输入以下命令行创建仓库&#xff08;一般来说&#xff0c;我觉得直接在code workspace得地方创建git仓库就可以了&#xff0c;这…

自从用了这些监控工具,我连续几天没睡好觉!

大家好&#xff0c;我是程序员鱼皮&#xff0c;今天分享一些很实用的系统监控告警工具。 为什么要用监控告警&#xff1f; 说到监控告警&#xff0c;没有企业开发经验的同学非常容易忽视它&#xff0c;甚至会有同学觉得没有必要&#xff0c;大不了出了 Bug 再修就是了。 这种…

MySQL —— 库,数据类型 与 表

库与基础操作 1.1 查看数据库 使用 show databases; 可以查看当前 MySQL 目前有多少个数据库 5 rows 表示有 5 行&#xff0c;这里是表示的是有效的数据&#xff0c;不包括 第一行的指引 set 表示结果集合 0.01 sec 表示这个 sql 语句一共运行了0.01 秒&#xff0c;一般情况…

滚珠花键:新能源汽车传动系统的核心动力传递者

在日常生活中&#xff0c;汽车已经成为了必不可少的交通工具&#xff0c;尤其是新能源汽车。而滚珠花键作为传动系统中的重要组成部分&#xff0c;在传动系统方面的作用不容忽视。 随着科技的不断发展&#xff0c;汽车行业也在不断进步&#xff0c;滚珠花键作为高精度的机械传动…

C#中的wpf基础

在WPF中&#xff0c;Grid 是一种非常强大的布局控件&#xff0c;用于创建网格布局。它允许你将界面划分为行和列&#xff0c;并将控件放置在这些行和列中。 以下是一些关键点和示例&#xff0c;帮助你理解 WPF 中的 Grid&#xff1a; 基本属性 RowDefinitions&#xff1a;定义…

中国人工智能最好50所大学排名-2024年最强学校名单

人工智能最强的学校包含&#xff1a;清华大学、上海交通大学、南京大学、西安电子科技大学、电子科技大学、中国科学技术大学、哈尔滨工业大学、华中科技大学、东南大学、浙江大学等学校。这些都是人工智能专业排名全国前十的名牌大学。 圆梦小灯塔将在下文继续为2024年高考生…

详解基于百炼平台及函数计算快速上线网页AI助手

引言 在当今这个信息爆炸的时代&#xff0c;用户对于在线服务的需求越来越趋向于即时性和个性化。无论是寻找产品信息、解决问题还是寻求建议&#xff0c;人们都期望能够获得即时反馈。这对企业来说既是挑战也是机遇——如何在海量信息中脱颖而出&#xff0c;提供高效且贴心的…

MySQL系列之--关系型数据库以及SQL语句分类之DDL数据库和表的操作

文章目录 前言关系型数据库&#xff08;RDBMS&#xff09;关系型数据库的特点 MySQL数据模型SQL介绍基本语法规则SQL语句的分类DDL的介绍DDL的数据库操作DDL的表操作 前言 上一节MySQL系列之–详细安装教程和启动方法中介绍了MySQL如何安装&#xff0c;以及如何启动和客户端连接…

现代前端架构介绍(第一部分):App是如何由不同的构建块构成的

远离JavaScript疲劳和框架大战&#xff0c;了解真正重要的东西 几周前&#xff0c;我的同事们对我们的前端架构、代码结构和面临的挑战很感兴趣。在做了几次关于如何构建可扩展且健壮的前端的演讲后&#xff0c;我觉得把它们都总结一下并与社区分享我们的策略是一个不错的主意。…

内网穿透--meterpreter端口转发实验

实验背景 通过公司带有防火墙功能的路由器接入互联网&#xff0c;然后由于私网IP的缘故&#xff0c;公网无法直接访问内部主机&#xff0c;则需要通过已连接会话&#xff0c;代理穿透访问内网主机服务。 实验设备 1.路由器一台 2.内网 Win 7一台 3.公网 Kali 一台 4.网络 …

SuccBI+低代码文档中心 — 低代码应用(SuccAP)(概论)

概述&#xff1a; 低代码是什么&#xff1f; 低代码就是通过易用的、可视化的操作、加上少量的代码或脚本的方式快速的搭建业务应用。 低代码的优势&#xff1f; 低代码可以提升开发人员的效率&#xff0c;也可以让非开发人员也能进行应用开发。 低代码的分类&#xff1a;…

『康之泉活水馆』手游:打造夏日梦幻水世界

设计背景 夏日的热浪与城市的喧嚣困扰着忙碌奔波的人群&#xff0c;康之泉活水馆&#xff0c;作为多功能的室内水上乐园&#xff0c;以其独特的魅力&#xff0c;成为夏日避暑的理想之地&#xff0c;让身心得以彻底放松。 设计理念 优联前端以康之泉品牌IP形象“康康”为灵感&a…

计算机基础(Windows 10+Office 2016)教程 —— 第4章 计算机网络与Internet(上)

第4章 计算机网络与Internet 4.1 计算机网络概述4.1.1 计算机网络的定义4.1.2 计算机网络的发展4.1.3 计算机网络的功能4.1.4 计算机网络体系结构和TCP/IP 参考模型 4.2 计算机网络的组成和分类4.2.1 计算机网络的组成4.2.2 计算机网络的分类 4.3 网络传输介质和通信设备4.3.1 …

奇安信高管合计套现7.7亿,总裁个人套现1.9亿

【文末送&#xff1a;技战法】 昨天网安一哥&#xff0c;奇安信发布《关于中电金投增持公司股份暨持股 5% 以上股东协议转让公司股份的权益变动的提示性公告》&#xff0c;公告显示中国电子将再次收购奇安信5%的股份。 公告显示&#xff0c;奇安壹号合伙人中&#xff1a;天津…

24年电赛——自动行驶小车(H题)基于 CCS Theia -陀螺仪 JY60 代码移植到 MSPM0G3507(附代码)

前言 只要搞懂 M0 的代码结构和 CCS 的图形化配置方法&#xff0c;代码移植就会变的很简单。因为本次电赛的需要&#xff0c;正好陀螺仪部分代码的移植是我完成的。&#xff08;末尾附全部代码&#xff09; 一、JY60 陀螺仪 JY60特点 1.模块集成高精度的陀螺仪、加速度计&…

day12 多线程

目录 1.概念相关 1.1什么是线程 1.2什么是多线程 2.创建线程 2.1方式一&#xff1a;继承Thread类 2.1.1实现步骤 2.1.2优缺点 2.1.3注意事项 2.2方式二&#xff1a;实现Runnable接口 2.2.1实现步骤 2.2.2优缺点 2.2.3匿名内部类写法 2.3方式三&#xff1a;实现cal…

Cesium 相机控制器(1)-wheel 实现原理简析

Cesium 相机控制器(1)-wheel 实现原理简析 已经做大量简化, 不是代码最终的样子. Viewer┖ CesiumWidget┖ ScreenSpaceCameraController(_screenSpaceCameraController)┣ CameraEventAggregator(_aggregator) // 相机事件代理┃ ┖ ScreenSpaceEventHandler(_eventHandler…

Notion爆红背后,笔记成了AI创业新共识?

在数字化时代&#xff0c;笔记软件已成为我们记录、整理和创造知识的得力助手。本文将带您深入了解Notion以及其他五个AI笔记产品&#xff0c;它们如何通过AI重塑笔记体验&#xff0c;满足我们快速记录、捕捉灵感、智能整理、情感陪伴和自动撰写文章的五大核心需求。 ———— …