c语言回顾-结构体(2)
前言
前面讲了结构体的概念,定义,赋值,访问等知识,本节内容小编将讲解结构体的内存大小的计算以及通过结构体实现位段,话不多说,直接上干货!!!
1.结构体内存对齐
说到计算结构体的大小,就要了解结构体内存对齐原则。
结构体内存对齐是指在内存中存储结构体变量时,根据结构体成员的类型和大小,按照一定的规则进行内存对齐,以提高内存访问效率。
1.1对齐规则
1. 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数与该成员变量大小的较小值。- VS 中默认的值为 8- Linux中 gcc 没有默认对齐数,对齐数就是成员自身的大小3. 结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的)的整数倍。4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
举例说明
struct S1
{char c1;//1int i;//4char c2;//1
};
printf("%d\n", sizeof(struct S1));
12字节为最大对齐数4的倍数,所以结构体大小为12
例2
struct S3
{double d;char c;int i;
};
printf("%d\n", sizeof(struct S3));
16个字节刚好为最大对齐数(double)的整数倍,所以结构体大小为16
例3(结构体嵌套)
struct S4
{char c1;struct S3 s3;double d;
};
printf("%d\n", sizeof(struct S4));
32字节为最大对齐数8的倍数,所以结构体大小为32
1.2为什么存在内存对齐
struct S1
{
char c1;//1
int i;//4
char c2;//1
};
struct S2
{
char c1;//1
char c2;//1int i;//4
};
printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));
1.3修改默认对齐数
#pragma 这个预处理指令,可以改变编译器的默认对齐数。
#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{char c1;int i;char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认
int main()
{//输出的结果是什么?printf("%d\n", sizeof(struct S));return 0;
}
2.结构体传参
#include <stdio.h>
struct S
{int data[1000];int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{for(int i=0;i<4;i++){printf("%d ",s.data[i]);}printf("\n%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{for(int i=0;i<4;i++)printf("%d ",ps->data[i]);printf("\n%d\n", ps->num);
}
int main()
{print1(s); //传结构体print2(&s); //传地址return 0;
}
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
3.结构体实现位段
3.1什么是位段
位段(Bit-fields)是一种在C语言中用于节省内存的技术,它允许程序员定义一个结构体或联合体中的成员变量,这些成员变量的大小以位为单位,而不是以字节为单位。位段可以用来表示那些只需要少量位来存储的数据,例如标志位或者状态位。
位段的定义方式是在结构体或联合体中使用冒号(:)指定成员变量所占用的位数。
位段的声明和结构是类似的,有两个不同:1. 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以选择其他类型。2. 位段的成员名后边有一个冒号和一个数字。
struct A
{int _a:2;//占2个两个bit位int _b:5;int _c:10;int _d:30;
};
位段式结构中的位可以理解二进制位
在C语言中,位段的大小取决于编译器和硬件平台的具体实现。通常,位段的大小是按照字节对齐的,但是位段内部的位数是按照定义的位数来分配的。
上述位段占了47位,对齐6个字节,也就是48位,但是用sizeof测试时出来是8字节
在大多数系统中,位段会按照最接近的字节边界对齐。由于这个结构体总共占用了47位,它可能会被对齐到6个字节(48位),因为这是最接近47位的字节数,并且可以容纳所有的位段。
然而,位段的确切大小和对齐方式取决于编译器和硬件平台的具体实现。在某些系统上,如果位段不能恰好填充到一个字节,编译器可能会分配额外的位来填充到下一个字节边界。此外,如果位段的大小超过了单个整数类型(通常是32位或64位)的位数,编译器可能会将它们分割到多个整数中。
3.2位段的内存分配
1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
struct S
{char a:3;char b:4;char c:5;char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
接下来通过画图来看内存空间的开辟分配
1.在申请的一块内存中,bit位是从左到右,还是从右到左使用,是不确定的,VS是从右到左
2.剩余的空间,不足下一个成员使用的时候,是浪费?还是继续使用?VS采取浪费
ok,回到最上面那个位段求大小
struct A
{
int _a:2;//占2个两个bit位
int _b:5;
int _c:10;
int _d:30;
};
一次性申请4个字节,第一次用17个bit位,剩余15个不够用,根据VS的规则,采取浪费,所以再次申请4个字节存取剩下的_d数据。
即该位段大小为8
3.3位段的跨平台问题
1. int 位段被当成有符号数还是无符号数是不确定的。2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。eg:32位或者64位int的长度占4个字节,16位int是2个字节3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
3.4位段的应用
IP数据报的格式,我们可以看到其中很多的属性只需要几个bit位就能描述,这里使用位段,能够实现想要的效果,也节省了空间,这样网络传输的数据报大小也会较小一些,对网络的畅通是有帮助的。
3.5位段使用的注意事项
struct A {int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};
int main() {struct A sa = {0};scanf("%d", &sa._b);//这是错误的
//正确的⽰范int b = 0;scanf("%d", &b);sa._b = b;return 0;
}
下面是搜集的位段注意事项的其他总结
1. 可移植性问题:位段的行为和大小可能因编译器和硬件平台而异。因此,位段不具有可移植性,应该避免在需要跨平台兼容的代码中使用位段。
2. 对齐和大小:位段的对齐方式和大小取决于编译器的实现。编译器可能会将位段对齐到字节边界,这可能导致额外的填充位。因此,不应该假设位段的确切大小,除非编译器文档明确说明了位段的行为。
3. 位段类型:位段通常使用 `unsigned int` 或 `int` 类型定义,但编译器可能会允许其他整数类型。然而,使用非标准类型可能会降低代码的可移植性。
4. 位段操作:位段的操作不如普通变量直观,因为它们涉及到位的操作。在访问和修改位段时,需要小心处理位操作,以避免错误。
5. 位段顺序:位段在内存中的存储顺序可能因编译器而异。有些编译器可能按照位段的定义顺序存储,而其他编译器可能按照相反的顺序存储。
6. 位段跨越字节边界:如果一个位段的大小超过了单个字节的位数,它将会被分割到两个字节中。这可能会导致难以预测的内存布局。
7. 位段的符号性:如果使用 `int` 类型定义位段,位段可能是带符号的。这意味着位段的最高位可能被解释为符号位,这可能会导致意外的行为。为了确保位段是无符号的,应该使用 `unsigned int` 类型。
8. 位段的访问:在某些平台上,访问位段可能比访问普通变量更慢,因为位段需要额外的位操作。
9. 位段的初始化和赋值:位段的初始化和赋值可能需要特殊的位操作,因为它们不是以字节为单位进行操作的。
10. 位段的限制:位段不能用于数组或指针,也不能用于结构体或联合体的嵌套定义。
在使用位段时,应该仔细考虑这些注意事项,并确保代码的可读性、可维护性和正确性。如果可能,应该考虑使用其他技术,如位掩码或位操作函数,来代替位段,以提高代码的可移植性和可读性。
OK,本节内容到此结束,支持小编的留下你的关注,评论和点赞吧!!!
相关文章:

c语言回顾-结构体(2)
前言 前面讲了结构体的概念,定义,赋值,访问等知识,本节内容小编将讲解结构体的内存大小的计算以及通过结构体实现位段,话不多说,直接上干货!!! 1.结构体内存对齐 说到计…...

Prometheus常见exporter安装部署
Prometheus常见exporter安装部署 在稳定性环境的监控当中需要收集各种各样的数据,这样的数据收集是通过各种exporter进行的,在这里我们进行最常用稳定性数据的收集exporter安装部署介绍。 node_exporter安装部署 node_exporter主要监控服务器本身的一…...

DGit的使用
将Remix连接到远程Git仓库 1.指定克隆的分支和深度 2.清理,如果您不在工作区上工作,请将其删除或推送至 GitHub 或 IPFS 以确保安全。 为了进行推送和拉取,你需要一个 PAT — 个人访问令牌 当使用 dGIT 插件在 GitHub 上推送、拉取、访问私…...

ElasticSearch学习篇13_《检索技术核心20讲》进阶篇之LSM树
背景 学习极客实践课程《检索技术核心20讲》https://time.geekbang.org/column/article/215243,文档形式记录笔记。 内容 磁盘和内存数据读取特点 工业界中数据量往往很庞大,比如数据无法全部加载进内存,无法支持索引的高效实时更新&…...

简单好用的C++日志库spdlog使用示例
文章目录 前言一、spdlog的日志风格fmt风格printf风格 二、日志格式pattern三、sink,多端写入四、异步写入五、注意事项六、自己封装了的代码usespdlog.h封装代码解释使用示例 前言 C日志库有很多,glog,log4cpp,easylogging, eas…...

python 方法运行计时装饰模式实现
在代码开发过程中,需要记录方法的执行时间,每个方法都硬代码也可以实现,但是不是最好的方式,考虑到设计模式和模版代码,通过装饰模式实现方法运行计时 在Python中,装饰器可以接受参数,这样可以…...

【权威出版/投稿优惠】2024年水利水电与能源环境科学国际会议(WRHEES 2024)
2024 International Conference on Water Resources, Hydropower, Energy and Environmental Science 2024年水利水电与能源环境科学国际会议 【会议信息】 会议简称:WRHEES 2024 大会时间:点击查看 截稿时间:点击查看 大会地点:…...

阿赵UE引擎C++编程学习笔记——场景加载和切换
大家好,我是阿赵。 继续学习UE引擎,这次来学习一下切换和加载场景的各种做法。 一、 蓝图实现 1、 切换关卡 所谓切换关卡,就是从当前关卡进入到一个新的关卡, 旧关卡的数据将会被放弃。进入新的关卡后,将会执行…...

【LLM之RAG】RAFT论文阅读笔记
研究背景 论文针对的主要问题是如何将预训练的大型语言模型(LLMs)适应特定领域的检索增强生成(RAG)。这些模型通常在广泛的文本数据上进行预训练,已经表现出在广义知识推理任务上的优越性能。然而,在特定领…...

【Android】使用Binder(AIDL)实现利用自定义Bean进行的进程间通信(二)
项目前置 这是我之前写的关于Binder的一些知识点和使用基本数据类型在通信的文章,感兴趣的可以看一下: Binder(一)Binder的介绍和AIDL使用Binder的实例 项目目标 在两个APP之间进行数据传递,使用Android推荐的Binder通讯&#…...

HTTP中get与post的区别?在传输数据类型上有什么区别?【面试】
HTTP中的GET和POST是两种最常见的请求方法,它们在数据传输和使用场景上有一些关键的区别: GET请求: 数据传输方式:GET请求将数据附加在URL之后,形成查询字符串(namevalue的形式),数…...

「51媒体-年中大促」天津有哪些媒体资源-媒体宣传服务公司
传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 天津的媒体资源相当丰富,涵盖了报纸、电视、广播、新闻门户网站、央媒驻天津机构、视频媒体以及全国媒体资源等多个方面。以下是详细的媒体资源分类和具体信息: 一…...

Thinkphp校园新闻发布系统源码 毕业设计项目实例
Thinkphp校园新闻发布系统源码 毕业设计项目实例 校园新闻发布系统模块: 用户模块:注册,登陆,查看个人信息,修改个人信息,站内搜索,新闻浏览等功能, 后台管理员模块:会员…...

前端老古董execCommand——操作 选中文本 样式
文章目录 ⭐前言⭐exe command api用法💖 example示例💖 测试效果 ⭐execommand和getSelection 的联系⭐总结⭐结束 ⭐前言 大家好,我是yma16,本文分享关于 前端老古董execCommand——操作选中文本。 execommand 当一个 HTML 文…...

elementui写一个自定义的rangeInput的组件
组件定义 使用el-row确保元素都在一行上对外暴露的prop是minValue和maxValue,但是不建议直接使用,使用计算属性minValueComputed和maxValueComputed更改计算属性的值的不要直接更改计算属性,也不要直接更改原本的prop,通知外层的父…...

护眼灯哪些牌子好?一文刨析护眼灯怎么选择!
护眼灯哪些牌子好?护眼台灯作为对抗视力挑战的一种方法,逐渐赢得了众多家长的青睐。这些台灯利用尖端光学技术,发出柔和且无刺激的照明,有助于保护眼睛不受伤害。它们不但可以调节亮度和色温,打造一个舒适且自然的阅读…...

抖音短剧看剧系统是怎么做的?怎么样搭建上线运营?
前言: 当前热门短剧已深入大家的日常,针对一些好的短剧更是吸金无数。今天给大家介绍一下短剧这个项目整个运作模式。 一、一部短剧是怎么样呈现到观众眼前的? 首先影视作品公司拍摄剪辑好短剧 ,弄好一切审核后,放到…...

2024.06.06校招 实习 内推 面经
绿*泡*泡VX: neituijunsir 交流*裙 ,内推/实习/校招汇总表格 1、校招 | 追觅科技2025届校园招聘/正式启动! 校招 | 追觅科技2025届校园招聘正式启动! 2、校招&实习&社招 | 博世海外招聘—德国/专场正式启动࿰…...

神经网络模型---ResNet
一、ResNet 1.导入包 import tensorflow as tf from tensorflow.keras import layers, models, datasets, optimizersoptimizers是用于更新模型参数以最小化损失函数的算法 2.加载数据集、归一化、转为独热编码的内容一致 3.增加颜色通道 train_images train_images[...,…...

Linux之网络编程
Linux之网络编程 TCP协议 TCP(Transmission ControlProtocol) : 传输控制协议,是一个 面向连接的、可靠的、基于字节流的传输层的协议。TCP 协议建立的是一种点到点的,一对一的可靠连接协议 特点: 数据无丢失数据无失序数据无错误数据无重…...

opencascade AIS_InteractiveContext源码学习1
AIS_InteractiveContext 前言 交互上下文(Interactive Context)允许您在一个或多个视图器中管理交互对象的图形行为和选择。类方法使这一操作非常透明。需要记住的是,对于已经被交互上下文识别的交互对象,必须使用上下文方法进行…...

TIA博途 WinCC下载到面板时,提示错误消息:“装载过程终止由于传输错误:8020AB001A06FFF4!”的解决办法
TIA博途 WinCC下载到面板时,提示错误消息:“装载过程终止由于传输错误:8020AB001A06FFF4!”的解决办法 这个错误信息是由于缺少设备镜像无法下载到操作面板而导致的。 当使用 TIA V15.1 Update 4 和 Update 5 组态 TP1000F Mobile 时,请遵守特别注意事项。 问题 在编译一个…...

【MySQL】聊聊数据库是如何保证数据不丢的
对于一个存储系统来说,其中比较关键的核心组件包含,网络、存储模型、持久化、数据结构等。而数据如何保证不丢失,对于不同的存储系统来说,比如Redis采用AOF和RDB的方式进行混合使用,而MySQL采用日志进行保证。也就是re…...

GitLab教程(四):分支(branch)和合并(merge)
文章目录 1.分支(branch)(1)分支的概念(2)branch命令 2.合并(merge)(1)三个命令pullfetchmergegit fetchgit mergegit pull (2)合并冲…...

2021数学建模A题目–“FAST”主动反射面的形状调节
A 题——“FAST”主动反射面的形状调节 思路:该题主要是通过利用伸缩杆调整FAST反射面,给出合适的调整方案 程序获取 第一题问题思路与结果: 当待观测天体S位于基准球面正上方,结合考虑反射面板调节因素,确定理想抛物…...

华为---- RIP路由协议基本配置
08、RIP 8.1 RIP路由协议基本配置 8.1.1 原理概述 RIP(Routing Information Protocol,路由协议)作为最早的距离矢量IP路由协议,也是最先得到广泛使用的一种路由协议,采用了Bellman-Ford算法,其最大的特点就是配置简单。 RIP协议要求网络中…...

Android studio在Ubuntu桌面上 创建桌面图标,以及导航栏图标
Android studio在Ubuntu桌面上 创建桌面图标,以及导航栏图标 1. 下载Android studio for Lunux 免安装版本之后,解压 2. 通过控制台运行 ~/Documents/android-studio-2024.1.1.2-linux/android-studio/bin$ ./studio.sh 3. 选择菜单,Tools…...

JAVA云HIS医院管理系统源码 云HIS系统的应用场景
JAVA云HIS医院管理系统源码 云HIS系统的应用场景 云HIS是针对中小医疗健康机构推出的一套基于云端的诊所云HIS服务平台,包括内部管理系统、临床辅助决策系统、体检系统、客户管理与服务系统、健康管理系统、知识管理系统、医患沟通系统、线上营销系统、其他外部系…...

Handler机制
目录 一、简介二、相关概念解释2.1 Message(消息)2.2 Handler(处理器)2.2.1 Handler的构造方法2.2.2 Handler sendMessage()相关的方法2.2.3 Handler dispatchMessage()方法 2.3 Mes…...

鸿蒙实现金刚区效果
前言: DevEco Studio版本:4.0.0.600 所谓“金刚区"是位于APP功能入口的导航区域,通常以“图标文字”的宫格导航的形式出现。之所以叫“金刚区”,是因为该区域会随着业务目标的改变,展示不同的功能图标ÿ…...