【C语言】结构体内存对齐机制详解

目录

    • 一、前言
    • 二、结构体内存对齐规则
    • 三、实例解析

一、前言

在讲解结构体内存对齐机制之前,我们先来看1个例子:

typedef struct
{char sex;       //  性别int id;         //  学号char name[20];  //  姓名float score;    //  成绩char addr[30];  //  地址
}STU;int main(){STU student1;printf("%d\n",sizeof(student1));//  打印结构体变量所占内存长度return 0;
}

以上代码示例中定义了1个结构体student1,包含5个不同数据类型的成员,分别占用内存空间:
char sex:1 Byte
int id:4 Byte
char name[20]:20 Byte
float score:4 Byte
char addr[30]:30 Byte
根据结构体特性,结构体变量所占内存长度等于其各成员所占内存长度之和,因此这里打印的sizeof(student1)是否等于1+4+20+4+30=59呢?运行程序后结果如下:

size of student1: 64

为什么这里结构体变量student1所占内存长度是64而不是59呢?因为有结构体内存对齐机制的存在。这是很多新手程序猿很容易走进的一个误区,接下来我们一起来了解结构体对齐机制。

二、结构体内存对齐规则

  1. 结构体第一个成员的地址即为该结构体变量的地址。
  2. 结构体成员内存对齐时,其存放的内存单元大小为min{有效对齐值,指定对齐值}的最小整数倍。
    注意:
    自身对齐值: 结构体变量里每个成员自身所占内存大小
    指定对齐值: 使用宏定义#pragma pack(N) 其中N的值即为指定对齐值。N必须是2的幂次方,例如:1,2,4,8等,如果没有使用宏定义指定对齐值,则指定对齐值取决于操作系统位数,32位操作系统默认指定对齐值为4,64位操作系统默认指定对齐值为8
    有效对齐值: 有效对齐值为min{自身对齐值,指定对齐值}。
  3. 结构体总占用内存大小为min{成员最大自身对齐值,指定对齐值}的整数倍。

三、实例解析

以下示例代码均在64位Windows操作系统下运行
1、使用默认指定对齐值
使用文章前言的代码分析:

#include <stdio.h>typedef struct
{char sex;       //  性别int id;         //  学号char name[20];  //  姓名float score;    //  成绩char addr[30];  //  地址
}STU;int main(){STU student1;printf("size of student1: %d\n",sizeof(student1));    //  打印结构体变量所占内存长度return 0;
}
成员自身对齐值指定对齐值有效对齐值
sex18min{1,8}
id48min{4,8}
name[20]18min{1,8}
score48min{4,8}
addr[30]18min{1,8}

结构体总体对齐时,应按照成员自身对齐值的最大值的整数倍对齐,这里程序在没有宏定义指定默认对齐值的情况下,对齐值就应该是min{4,8},也就是4字节,这种情况下,该结构体每一个成员开辟的存储空间都为4字节。

成员实际占用内存大小系统开辟内存空间大小
sex14
id44
name[20]2020
score44
addr[30]3032

在这里插入图片描述

成员id、name[20]、score实际占用内存大小刚好是对齐值4的整数倍,而成员sex则需要开辟4字节空间,实际只占用1字节,剩余3字节未使用;成员addr[30]需要开辟32字节空间,实际使用30字节,剩余2字节未使用。因此打印结构体变量student1所占内存大小为64字节。

2、修改默认指定对齐值
在上述示例基础之上修改默认指定对齐值:

#include <stdio.h>
#pragma pack(1) //  指定默认对齐值为1
typedef struct
{char sex;       //  性别int id;         //  学号char name[20];  //  姓名float score;    //  成绩char addr[30];  //  地址
}STU;
#pragma pack()  //  取消修改对齐值
int main(){STU student1;printf("size of student1: %d\n",sizeof(student1));    //  打印结构体变量所占内存长度return 0;
}

此时程序输出结果为:

size of student1: 59

以上代码使用#pragma pack(1) 将默认指定对齐值修改为1,那么结构体成员将会按照1字节做内存对齐。

成员实际占用内存大小系统开辟内存空间大小
sex11
id44
name[20]2020
score44
addr[30]3030

此时打印结构体变量student1所占内存长度就为59。

如果将默认指定对齐值修改为2呢?

#include <stdio.h>
#pragma pack(2) //  指定默认对齐值为1
typedef struct
{char sex;       //  性别int id;         //  学号char name[20];  //  姓名float score;    //  成绩char addr[30];  //  地址
}STU;
#pragma pack()  //  取消修改对齐值
int main(){STU student1;printf("size of student1: %d\n",sizeof(student1));    //  打印结构体变量所占内存长度return 0;
}

程序输出结果:

size of student1: 60
成员实际占用内存大小系统开辟内存空间大小
sex12
id44
name[20]2020
score44
addr[30]3030

这个时候除了成员sex之外,其余成员的自身对齐数均是2的整数倍,因此成员sex需要根据对齐数2进行内存对齐,开辟2字节空间存储,但实际只使用1字节空间,最后打印结构体变量student1的所占内存长度为60。

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

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

相关文章

基于HTML5架构的综合管廊系统网络结构设计

摘 要&#xff1a;从网络拓扑结构、开放式实时以太网协议、控制层系统配置方面介绍了综合管廊的系统网络架构设计&#xff0c;分析了无线网络特性&#xff0c;阐述了基于HTML5架构所能实现的功能的初步构想&#xff0c;以便于综合管廊运维人员巡检&#xff0c;确保管廊本体安全…

基于TensorFlow+CNN+协同过滤算法的智能电影推荐系统——深度学习算法应用(含微信小程序、ipynb工程源码)+MovieLens数据集(七)

目录 前言总体设计系统整体结构图系统流程图 运行环境模块实现1. 模型训练1&#xff09;数据集分析2&#xff09;数据预处理3&#xff09;模型创建4&#xff09;模型训练5&#xff09;获取特征矩阵 2. 后端Django3. 前端微信小程序1&#xff09;小程序全局配置文件2&#xff09…

怎样找到NPM里面开源库下载地址

场景 最近帮忙找一个开源库地址。这里以vue/language-core为例子。 解决 https://registry.npmmirror.com/vue/language-core/1.8.13这里就是如下格式&#xff1a; https://registry.npmmirror.com/{包名}/{版本号}打开这个页面后&#xff0c;得到开源库下载地址&#xff0c…

使用Postman如何在接口测试前将请求的参数进行自定义处理

1、前言 当我们使用 Postman 进行接口测试时&#xff0c;对于简单的不需要处理的接口&#xff0c;直接请求即可&#xff0c;但是对于需要处理的接口&#xff0c;如需要转码、替换值等&#xff0c;则就麻烦一些&#xff0c;一般我们都是先手动把修改好的值拷贝到请求里再进行请…

C++【个人笔记1】

1.C的初识 1.1 简单入门 #include<iostream> using namespace std; int main() {cout << "hello world" << endl;return 0; } #include<iostream>; 预编译指令&#xff0c;引入头文件iostream.using namespace std; 使用标准命名空间cout …

IC芯片测试:如何对芯片静态功耗进行测试?

静态功耗也叫静态电流&#xff0c;是指芯片在静止状态下的电流或者是指芯片在不受外界因素影响下自身所消耗的电流。静态功耗对于芯片来说是衡量一款芯片的功耗与效率非常重要的指标。 传统手动测试静态功耗只需在芯片的输入端串上一台万用表&#xff0c;然后对芯片各个端口添加…

Vue 安装与创建第一Docker的项目

1. 下载nodejs 并且安装 Node.js 2. 打开命令窗口&#xff0c;验证是否安装成功 C:\Users\Harry>node -v v18.16.0C:\Users\Harry>npm -v 9.5.1 3. 安装Vue CLI C:\Users\Harry>npm install -g vue/cli 经过不算漫长的等待&#xff0c;你的Vue CLI就装好了。确认一下…

嵌入式Linux驱动开发(I2C专题)(七)

使用GPIO操作I2C设备_IMX6ULL 参考资料&#xff1a; Linux文档 Linux-5.4\Documentation\devicetree\bindings\i2c\i2c-gpio.yamlLinux-4.9.88\Documentation\devicetree\bindings\i2c\i2c-gpio.txt Linux驱动源码 Linux-5.4\drivers\i2c\busses\i2c-gpio.cLinux-4.9.88\driv…

nodeJs+Mongodb+mongoose入门

nodeJsexpressMongodbmongooseNavicat 自我记录 一、简介 1.1 Mongodb 是什么 MongoDB 是一个基于分布式文件存储的数据库&#xff0c;官方地址 https://www.mongodb.com/ 1.2 数据库是什么 数据库&#xff08;DataBase&#xff09;是按照数据结构来组织、存储和管理数据…

人机交互——对话管理

​人机交互中的对话管理主要是指在人机交互过程中&#xff0c;对交互的对话内容和流程进行管理&#xff0c;以实现自然、流畅、高效的交互效果。对话管理包括对话状态追踪、对话策略优化等多个方面。 对话状态追踪是指对当前对话的状态进行跟踪&#xff0c;例如对用户输入的语…

【linux】shell脚本调试

前几天的一篇linux定时删除服务器日志 &#xff0c;有人读了&#xff0c;私信问题。说我写了脚本了&#xff0c;怎么去调试一下&#xff0c;类似于代码的debug。 那我们今天来聊聊。 执行脚本命令是&#xff1a; sh 脚本名 sh dele_log2.sh 执行并输出。 Shell 脚本调试选…

mysql 导入sql文件

mysql 导入sql文件 sudo mysql -uroot -p123456 -h127.0.0.1 sudo mysql -uroot -p123456 -h127.0.0.1然后 show databases;然后 use 数据库名称; 然后 source 20230920031001.sql;如果不加 -h127.0.0.1 可能会出现错误 /var/lib/mysql.sock error 通过 navicat导入的话&am…