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

news2024/11/14 20:14:57

🦄个人主页:小米里的大麦-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.coloradmin.cn/o/1976594.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开源个性化自托管服务仪表板:Dashy

**Dashy&#xff1a;**一站式管理&#xff0c;个性化展现- 精选真开源&#xff0c;释放新价值。 概览 Dashy是一个创新的自托管仪表板解决方案&#xff0c;旨在为用户提供一个集中管理多个在线服务的平台。通过直观的界面设计&#xff0c;Dashy允许用户快速访问他们的自托管应…

【C++】内联函数vs宏 nullptr

目录 宏的优缺点分析概念回顾宏的缺点宏的优点 内联函数&#xff08;inline&#xff09;inline函数的定义和声明总结 宏的优缺点分析 概念回顾 下面是宏的申明方式&#xff1a; #define name( parament-list ) stuff //其中的 parament-list 是一个由逗号隔开的符号表&#x…

一个能够在网上爬取思维导图的python小程序

这个小程序是为需要从网上爬取思维导图的朋友写的,时间久了怕被遗忘在垃圾箱里,所以贴出来,给需要的同学使用。 河西石原创地址:https://haigear.blog.csdn.net/article/details/140878039 二、使用方法及流程介绍 简单的说明一下使用的方法: 1、在网上找到自己需要的思…

GBase8c psycopg2安装(centos6)

GBase8c psycopg2安装(centos6) 安装步骤&#xff1a; [rootcentos6 ~]# cd /opt/python/ [rootcentos6 python]# ls psycopg2-2.7.7.tar.gz [rootcentos6 python]# tar -zxf psycopg2-2.7.7.tar.gz [rootcentos6 python]# cd psycopg2-2.7.7 # 安装命令 [rootcentos6 psycop…

【C++:jsoncpp库的配置CMAKE的安装】

CMAKE的安装&#xff1a; 安装路径&#xff1a;Download CMake安装就是无脑Next跳出以下窗口以上步骤完了之后&#xff0c;页面如此&#xff0c;然后点击generate jsoncpp库的配置&#xff1a; 打开生成的源文件所在路径&#xff0c;找到名为jsoncpp.sln的文件&#xff0c;以vs…

大数据信用报告怎么查?有哪些注意事项?

大数据信用对于有资金周转的人来说是比较重要的&#xff0c;主要由于大数据信用无形的被不少机构用于贷前风控&#xff0c;无论是机构要求的还是自查&#xff0c;提前了解大数据信用情况是常规操作&#xff0c;那大数据信用报告如何查询?有哪些需要注意的呢?本文详细为大家讲…

【Kubernetes】k8s集群的资源发布方式

目录 一.常见的发布方式 二.如何实现 1.滚动升级 2.蓝绿升级 3.实现金丝雀发布&#xff08;灰度发布&#xff09; 一.常见的发布方式 蓝绿发布&#xff1a;两套环境交替升级&#xff0c;旧版本保留一定时间便于回滚。优点 用户无感知&#xff0c;部署和回滚速度较快缺点 …

使用 Python 对雷达卫星 sar 图像进行降噪的三种方法

合成孔径雷达 (SAR) 图像广泛应用于各种领域(航空航天、军事、气象等)。问题是这种图像在其原始格式中受到噪点的影响。虽然这些图像通常也是沉重的文件,但从科学的角度来看,有效地对其进行去噪的任务似乎既具有挑战性,又在现实世界中非常有用。 卫星图像有两大类: 光学…

嵌入式C++、QML与MQTT:智能化农业灌溉管理系统设计思路(代码示例)

目录 一、项目概述 二、系统架构 三、环境搭建 1. 硬件环境 2. 软件环境 四、代码实现 1. 硬件端代码示例 2. 软件端代码示例 a. 后端代码&#xff08;Node.js MQTT&#xff09; b. 前端代码&#xff08;QML&#xff09; 五、项目总结 一、项目概述 随着全球对农业…

Xinference如何注册自定义模型

环境&#xff1a; Xinference 问题描述&#xff1a; Xinference如何注册自定义模型 解决方案&#xff1a; 1.写个model_config.json&#xff0c;内容如下 {"version": 1,"context_length": 2048,"model_name": "custom-llama-3"…

Java 中的缓冲流

字符流 前面学习的字节流和字符流都是基本流&#xff0c;其中字符流的底层其实已经在内存中创建了一个长度为8192的字节数组作为缓存区。而字节流中则是没有的。 在内存中增加缓冲区的目的是为了减少内存与硬盘的交互的次数&#xff0c;因为这一操作比较耗时。 下面是一个图…

PixelMaster - 图片像素化终极利器 !

PixelMaster 是将普通图像转变为令人惊叹的像素艺术杰作的终极工具。非常适合艺术家、设计师和像素艺术爱好者&#xff01; https://apps.apple.com/app/pixelmaster-image-pixelator/id6502478442 为什么选择 PixelMaster&#xff1f; • 自定义像素形状&#xff1a;选择或导…

【Linux修行路】进度条小程序

目录 ⛳️推荐 一、预备知识 1.1 回车换行 1.2 缓冲区 二、倒计时 2.1 注意事项 三、进度条 3.1 源代码 3.2 代码分析 3.2 实际使用场景 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家…

安卓基本布局(上)

文章目录 LinerLayout线性布局RelativeLayout相对布局根据父容器定位根据兄弟组件定位margin偏移padding填充 LinerLayout线性布局 以水平或垂直的方式来排列界面中的控件。 常用属性详细描述orientation布局中组件的排列方式。horizonta&#xff1a;水平&#xff1b;vertical…

GPT-4o mini模型:小型化AI解决方案的创新应用案例

文章目录 每日一句正能量前言开发者视角初步接触与性能评估集成与开发流程成本效益分析创新应用案例面临的挑战与解决方案社区贡献与经验分享未来展望 性能评估处理能力与响应速度准确性与可靠性多功能性与灵活性资源效率可扩展性与集成性用户定制与微调 结论 成本效益分析初始…

C语言基础知识之函数指针和指针函数

函数指针和指针函数 函数指针和指针函数指向函数的指针返回指针值的函数指针函数和函数指针的区别 问题1_1代码1_1结果1_1 函数指针和指针函数 指向函数的指针 用函数指针变量调用函数 可以用指针变量指向整型变量、字符串、数组&#xff0c;也可以指向一个函数。一个…

ctfshow-web入门-sql注入(web176-web180)

目录 1、web176 2、web177 3、web178 4、web179 5、web180 1、web176 1 order by 4-- 闭合后简单判断了下字段数是 3 测试联合查询注入&#xff0c;存在关键字的过滤&#xff0c;包括 select 和 union &#xff08;后面经过测试实际只过滤了 select&#xff09; 大小写绕…

常⻅CMS漏洞

常⻅CMS漏洞 ⼀&#xff1a;WordPress ​ WordPress是⼀个以PHP和MySQL为平台的⾃由开源的博客软件和内容管理系统。WordPress具 有插件架构和模板系统。截⾄2018年4⽉&#xff0c;排名前1000万的⽹站中超过30.6%使⽤WordPress。 WordPress是最受欢迎的⽹站 内容管理系统。全…

Linux网络之多路转接——实用的epoll

目录 一、高级IO 1.1 概念 1.2 五种IO模型 1.3 小结 二、多路转接的实用派 2.1 epoll 接口 2.1.1 epoll_create 2.1.2 epoll_ctl 2.1.3 epoll_wait 2.2 epoll 底层原理 2.2.1 epoll_ctl 2.2.2 epoll_wait 2.2.3 epoll_create 三、 epoll 类的编写 3.1 类的框…

大数据-64 Kafka 高级特性 分区 分区重新分配 实测

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…