【C语言】结构体与内存对齐
前言
在本篇博客,我将介绍结构体类型,结构体变量的创建和初始化,重点介绍结构中存在的内存对齐。
结构变量
结构是一些值的集合,这些值被称为成员变量。结构的每个成员可以是不同类型的变量。
在理解结构的时候,我们可以结合我们此前学过的数组的概念去对比着理解。
数组: 结构:
一些值的集合 一些值的集合
1个或多个值 1个或多个值
值的类型是相同的 值的类型可以不同
而且,为了数组元素我们需要指明元素的位置(下标);而选择指定的结构成员我们是指明成员名字而非位置。
结构变量的创建
当我们需要存储彼此相关、类型不同的信息(数据)时,结构是一种很好的选择。
假如,我们现在记录一本书的相关信息,涉及到书名、作者名、价格、书号(id),这就是一个声明结构体变量的例子:
#include<stdio.h>
struct Book
{char book_name[20];char author[20];float price;char id[19];
}b3,b4;//可以在创建这个结构时直接创建变量//此时的b3、b4会作为全局变量int main()
{struct Book b1;//在main函数中创建,局部结构变量struct Book b2;
}
对结构声明的形式进行概括,是这样的:
struct tag//struct是结构体关键字,tag是标识符
{member - list;//成员列表
}variable-list;//变量列表
结构声明的括号后一定要记得加分号。
结构变量的初始化
我们可以在上面的代码基础上对4个结构变量初始化:
#include<stdio.h>
struct Book
{char book_name[20];char author[20];float price;char id[19];
}b3 = { "Call me by your name","Andre Acimen",29.1f,"9787513598255" },
b4 = {"Matilda","Roald Dahl",18.0f,"9787533259532"};int main()
{struct Book b1 = {"Flipped","Wendelin Van Draanen",16.99f,"9780375911743"};struct Book b2 = {"The Kite Runner","Khaled Hosseini",29.0f,"9787208061644"};
}
其实我们还可以不按照结构声明时成员的顺序来初始化,只要我们用"."来初始化:
struct Book
{char book_name[20];char author[20];float price;char id[19];
}b3 = { .author="Andre Acimen",.book_name = "Call me by your name",.id="9787513598255",.price = 29.1f },
b4 = {"Matilda","Roald Dahl",18.0f,"9787533259532"};
可以看到我们将b3的初始化顺序进行了调整。
结构体的特殊声明
其实,在声明结构时我们可以不完全声明:
struct
{char c;int i;double d;
}s1;
观察这个声明,可以发现我们没有了tag,也就是类型的名字。所以这也叫匿名结构体类型。
但这样写,用这个结构进行变量的声明就变成了一次性的。
现在,为了更详细地体现这种写法的特性,请看下面这段代码:
struct
{char c;int i;double d;
}s1;struct
{char c;int i;double d;
}*ps;int main()
{ps = &s1;return 0;
}
什么意思呢?
我们创造了一个结构类型,又用相同的成员创造了一个指针变量ps,所以ps指向一个与前面创造的匿名类型相同的结构体变量,既然是一模一样的结构类型,在main函数中我们想让ps指向s1,这是可以的吗?看似很合理。
但是,这是不允许的。因为编译器会认为最初声明的这个匿名结构与后面用来创建ps的结构不是同一个结构类型,所以不能赋值。这就是匿名结构声明的一个特点。
结构体内存对齐
这是关于结构体一个很重要的知识,需要运用的场景往往是计算结构体大小时。
结构体有大小吗?有的,但并不是每个成员类型的大小相加这么简单。
struct S1
{char c1;//1字节char c2;//1字节int n;//4字节
};
struct S2
{char c1;//1字节int n;//4字节char c2;//1字节
};
int main()
{printf("%zd\n", sizeof(struct S1));printf("%zd\n", sizeof(struct S2));return 0;
}
打印结果:

可以看到,结构体S1和S2中的成员大小的和都为6字节,而S1大小为8字节,S2大小为12字节。
这是为什么呢?
这是因为,结构体在内存中是存在对齐现象的。
在讲解什么是对齐现象前,我们先来讲一个概念:偏移量。
偏移量(Offset),在C语言中通常指的是结构体(struct)或联合体(union)内成员的位置。在结构体中,第一个成员的偏移量总是0。

(空格里代表的就是偏移量)
我们有一个宏,offsetof(type,member),可以计算结构成员相较于结构体变量起始位置的偏移量。 type就是要计算的结构,member是结构体成员名。
使用它需要一个头文件,#include<stddef.h>。
那么,我们现在就用它来计算一下S1中各个成员的偏移量吧:
#include<stdio.h>
#include<stddef.h>struct S1
{char c1;char c2;int n;
};
struct S2
{char c1;int n;char c2;
};
int main()
{struct S1 s1 = { 0 };printf("%zd\n", offsetof(struct S1, c1));printf("%zd\n", offsetof(struct S1, c2));printf("%zd\n", offsetof(struct S1, n));return 0;
}
运行结果:

所以,我们可以画出S1在内存的存储方式:

可以看到,偏移量为2和3的两个字节被浪费了。
现在,我们再次用offsetof来计算一下S2中各个成员的偏移量:
printf("%zd\n", offsetof(struct S2, c1));
printf("%zd\n", offsetof(struct S2, n));
printf("%zd\n", offsetof(struct S2, c2));

于是,我们又可以画出S2在内存中的存放:

但这时可不要以为就在c2结束的时候S2就结束了,可以看到我们上面sizeof得到的S2的字节数是12,而c2结束时才9个字节。也就是说,偏移量为9~11的3个字节也被我们浪费了。
这些现象究竟是为什么呢?这时就不得不说明对齐的规则了:
对齐规则
1.结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数:编译器默认的一个对齐数与该成员变量大小的较小值。
在vs中,默认为8;
Linux中gcc没有默认对齐数,对齐数就是成员自身大小。
3.结构体总大小为最大对齐数(结构体每个成员变量对齐数中最大的)的整数倍。
4.如果结构体嵌套了结构体,嵌套的结构体对齐到自己成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
用这些规则再回去看上面的S1和S2,你会发现都是对应得上的。但这里我们不妨再举一个例子:
#include<stdio.h>struct S3
{double d;char c;int i;
};
int main()
{printf("%zd\n", sizeof(struct S3));return 0;
}

那么我们现在看一下按照规则来分配我们的内存,得到的结果会不会是16:

所以我们根据对齐规则,内存中S3的分配应该是:

根据规则一步步走,我们就可以得到这个分配,确实是16个字节。
结构体嵌套情况:
为了体现规则的第四条,我们再来举一个结构体嵌套的例子:
#include<stdio.h>struct S3
{double d;char c;int i;
}s3;struct S4
{char c1;
//嵌套一个结构体S3,我们在前面已算出S3大小为16字节struct S3 s3;double d;
};int main()
{printf("%zd\n", sizeof(struct S4));//算S4大小return 0;
}

那么我们按照规则来画一下S4的内存,看看是不是32字节:

so,
可以看到,确实就是满足规则的。
为什么存在对齐
说到这里,你可能会好奇,为什么存在对齐呢?这里有两种主要的原因:
1.平台原因:
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址上取某些特定类型的数据,否则将抛出硬件异常。
2.性能原因:
数据结构(尤其是栈)应该尽可能地在自然界边界上对齐。原因在于,为了访问未对齐的内存,处理器可能要作两次内存访问;而对齐的内存访问仅需一次访问。(假设一个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。)
总的来说,结构体中的内存对齐总是拿空间来换取时间的做法。
(懒得用pro重画了,手写勿介意)

设计结构体的注意事项:
上面说了,结构体对齐现象是一种拿空间换时间的策略,而在设计结构体的时候,我们有一种可以既满足对齐又节省空间的做法:
让占用空间小的成员尽量集中在一起。(集中就行,不用把占用空间小的成员先声明)
(有兴趣的朋友可以自己画图看看)
修改默认对齐数
我们知道了默认对齐数的概念,那么我们是否能够修改默认对齐数呢?是可以的:
#pragma这个预处理指令,可以改变编译器的默认对齐数。
例如:
这是修改前的s的大小
这是修改后的:

可以看到,修改默认对齐数可以让我们的结构体大小变小。
到此,本篇博客内容就全部结束了,祝阅读愉快^-^
相关文章:
【C语言】结构体与内存对齐
前言 在本篇博客,我将介绍结构体类型,结构体变量的创建和初始化,重点介绍结构中存在的内存对齐。 结构变量 结构是一些值的集合,这些值被称为成员变量。结构的每个成员可以是不同类型的变量。 在理解结构的时候,我们…...
【机器学习】之 kmean算法原理及实现
基本概念 K-Means 聚类算法的目标是将数据集分成 ( K ) 个簇,使得每个簇内的数据点尽可能相似,而簇与簇之间尽可能不同。这种相似度是通过计算数据点与簇中心的距离来衡量的。 算法步骤 选择簇的数量 ( K ):随机选择 ( K ) 个数据点作为初…...
国产高边驱动HD70202Q替换英飞凌BTS7040-2
高边驱动也称之为高边开关,主要用于车内负载的驱动与开关,并对负载进行保护和诊断。高边驱动以高可靠性、灵活性、低功耗以及小型轻量等特点,正逐渐替代传统的保险丝、继电器等方案。 RAMSUN提供的HD70202Q车规级双通道智能高边驱动的输入控…...
2024年06月在线IDE流行度最新排名
点击查看最新在线IDE流行度最新排名(每月更新) 2024年06月在线IDE流行度最新排名 TOP 在线IDE排名是通过分析在线ide名称在谷歌上被搜索的频率而创建的 在线IDE被搜索的次数越多,人们就会认为它越受欢迎。原始数据来自谷歌Trends 如果您相…...
顺序表和链表基础操作的复习
顺序表 #include<iostream> using namespace std; 静态 //#define MAX_SIZE 50 //typedef int ElemType; //typedef struct //{ // int length; // ElemType nums[MAX_SIZE]; //}Sqlist; //动态: #define Init_SIZE 50 typedef int ElemType; typedef struct {int lengt…...
[C#]winform部署官方yolov10目标检测的onnx模型
【框架地址】 https://github.com/THU-MIG/yolov10 【算法介绍】 今天为大家介绍的是 YOLOv10,这是由清华大学研究团队最新提出的,同样遵循 YOLO 系列设计原则,致力于打造实时端到端的高性能目标检测器。 方法 创新 双标签分配策略 众所…...
hmcode硬件编程1
在/home/golemon/hmcode/applications/sample/wifi-iot/app内创建文件夹。 这里创建了d_6_3文件夹 . ├── BUILD.gn ├── d_6_3 │ ├── BUILD.gn │ └── lab.c ├── demolink │ ├── BUILD.gn │ └── helloworld.c ├── iothardware │ ├── B…...
[C++][CMake] set_target_properties called with incorrect number of arguments
1 简介 这篇文章将探讨了在使用CMake构建C项目时,调用set_target_properties函数时参数数量不正确所引发的问题。 2 错误案例 以下为可能发生错误的案例 include_directories (${CMAKE_SOURCE_DIR}/common) find_package(Threads)add_library (libusbmuxd SHARE…...
AdamW算法
AdamW算法是优化算法Adam的一个变体,它在深度学习中广泛应用。AdamW的主要改进在于它正则化方法的改变,即通过权重衰减(weight decay)而不是L2正则化,来控制模型参数的大小,从而提升了训练的稳定性和效果。…...
【c++进阶(二)】STL之string类的模拟实现
💓博主CSDN主页:Am心若依旧💓 ⏩专栏分类c从入门到精通⏪ 🚚代码仓库:青酒余成🚚 🌹关注我🫵带你学习更多c 🔝🔝 1.前言 本章重点 本章主要介绍一些关键接口的模拟实现ÿ…...
PHPStudy(xp 小皮)V8.1.1 通过cmd进入MySQL命令行模式
PHPStudy是一个PHP开发环境集成包,可用在本地电脑或者服务器上,该程序包集成最新的PHP/MySql/Apache/Nginx/Redis/FTP/Composer,一次性安装,无须配置即可使用。MySQL MySQL是一个关系型数据库管理系统,由瑞典 MySQL A…...
php反序列化初步了解
一、定义 序列化(串行化):将变量转换为可保存或传输的字符串的过程(通常是字节流、JSON、XML格式) 反序列比(反串行化):把这个字符串再转化成原始数据结构或对象(原来的…...
Windows系统电脑本地部署AI音乐创作工具并实现无公网IP远程使用
文章目录 前言1. 本地部署2. 使用方法介绍3. 内网穿透工具下载安装4. 配置公网地址5. 配置固定公网地址 前言 本文主要介绍如何在Windows系统电脑上快速本地部署一个文字生成音乐的AI创作工具MusicGPT,并结合cpolar内网穿透工具实现随时随地远程访问使用。 MusicG…...
玩转Linux进度条
准备工作: 一.关于缓冲区 首先,咱们先来一段有意思的代码: #include<stdio.h> #include<unistd.h> int main() {printf("you can see me");sleep(5);} 你可以在你的本地运行一下,这里我告诉大家运行结果…...
真国色码上赞,科技流量双剑合璧,商家获客新纪元开启
在数字化浪潮汹涌的今天,真国色研发团队依托红玉房网络科技公司的雄厚实力,凭借科技领先的核心竞争力,推出了创新性的商家曝光引流工具——码上赞。这款工具借助微信支付与视频号已有功能,为实体商家提供了一种全新的引流获客方式,实现了科技与商业的完美融合。 科技领先,流量黑…...
C++:特殊类设计和四种类型转换
一、特殊类设计 1.1 不能被拷贝的类 拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 C98: 1、将拷贝构造函数与赋值运算符重载只…...
(南京观海微电子)——屏幕材质及优缺点对比
LED/LCD LCD(Liquid Crystal Ddisplay)即“液晶显示器”,由两块偏光镜、两块薄膜晶体管以及彩色滤光片、光源(荧光灯)、显示面板组成的成像元器件。 LED(Light Emitting Diode)即“发光二极管…...
uniapp uni.showModal 出现点击没有反应
uni.showModal 里面有好些参数 点击后不弹出 是因为 出现了 null 或者undifind 字符 特别是content 里面 title: 提示, cancelColor: #000000, editable: true,//是否显示输入框 content: item.text?item.te…...
Vue3-VueRouter
客户端 vs. 服务端路由 服务端路由指的是服务器根据用户访问的 URL 路径返回不同的响应结果。当我们在一个传统的服务端渲染的 web 应用中点击一个链接时,浏览器会从服务端获得全新的 HTML,然后重新加载整个页面。 然而,在单页面应用中&a…...
【图像处理与机器视觉】频率域滤波
知识铺垫 复数 CRjI 可以看作复平面上的点,则该复数的坐标为(R,I) 欧拉公式 e j θ c o s θ j s i n θ e^{j\theta} cos \theta j sin \theta ejθcosθjsinθ 极坐标系中复数可以表示为: C ∣ C ∣ ( c o s…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...
