掌握C语言结构体,开启编程新世界

✨✨欢迎👍👍点赞☕️☕️收藏✍✍评论

个人主页:秋邱'博客

所属栏目:C语言

(感谢您的光临,您的光临蓬荜生辉)

前言

前面我们也涉及到了结构体的讲解,但是只是粗略的讲了一下。 接下里详细讲解。

1.0 结构体声明

struct tag {member-listmember-listmember-list  ...
} variable-list ;

结构体定义已经讲过了,但是不够全面,现在来重新看看,用具体的例子来理解结构体的声明,

struct num
{int num1;int num2;
}s1;///声明类型的同时定义变量是s1struct num s2;//定义结构体变量s2
struct num s3 = { 3,4 };//顺序初始化//代码2
struct book
{char name[20];int num;
}b1 = { {"zhuangji"},1001 };//顺序初始化struct book b2 = { .name = "tangmu",.num = 1002 };//指定顺序初始化//代码3
struct Node
{struct num;struct Node* next;
}n1 = { {1,2},NULL };//结构体嵌套定义struct Node n2 = { {5, 6}, NULL };//结构体嵌套初始化

以上初始化已经很详细了。 

2.0 匿名结构体

什么是匿名结构体呢?

匿名结构体就是省略类型标签(tag),只有成员变量,没有成员名称。无结构体类型,不能创建变量,只能在空号外定义变量,不能再创建变量。

struct//匿名结构体
{int a;char arr[20];
}Node = {1,"zhangsan"};//匿名初始化
//}Node = {.a=1,"lisi"};匿名选择初始化
int main()
{printf("%d %s",Node.a,Node.arr);return 0;
}

这就是一个匿名结构体, 以及它的初始化,打印方式跟正常结构体相似。

注意

匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。

3.0 自引用

struct Node
{int data;struct Node* next;//指针
}p;

这就是结构体自引用的表达式,这是正确的表达式。

倘若将代码改成这样,你认为合理吗?

struct Node
{int data;struct Node next;
}p;

这其实是不对的。 因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤ ⼩就会⽆穷的⼤,是不合理的。

4.0 内存对齐

我们知道了结构体的声明,以及初始化和使用,那么我们创建的结构体是多少字节呢?这也是一个常考的知识点。

4.1 对齐规则

⾸先得掌握结构体的对⻬规则:

1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处

2.其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。

  • 对⻬数=编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。
  • VS中默认的值为8
  • Linux中gcc没有默认对齐数,对对齐数就是成员自身的大小。

3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的 整数倍。

4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构 体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。 

什么意思呢?单看规则很难理解,我们直接上代码。

4.2 练习1

struct S1
{char c1;int i;char c2;};
int main()
{printf("%zd\n", sizeof(struct S1));return 0;
}

输出结果:

 12 

 那这个结果是怎么来的呢?

4.2.1 分析

注意:表中的一格代表一个字节。

偏移量:第一个字节相对于起始位置偏移量是0,第二个字节相对于起始位置偏移量是1。

假设我们从0开始存放,char c1的变量大小为1,所以存放一个字节(这时候的对齐数是1)。int i占四个字节,虽然vs默认值为8,但是int类型更小(这时候的对齐数是4),既偏移量1,2,3,都不是4的倍数,所以int放在偏移量为4的位置,char c2的大小是1,偏移量8是一的倍数,所以可以放。

你以为9就是struct S1的字节吗,那你就错了,我们还得对齐最大对齐数(4)。所以最后的结果就是12个字节。这样虽然会浪费空间,但是也是有一定的好处,我们之后再说。

4.3 练习2

struct S2
{char c1;char c2;int i;
};
int main()
{printf("%zd\n", sizeof(struct S1));return 0;
}

输出结果: 

4.3.1 分析 

 

char c1 占1个字节;char c2占1个自己,且对齐数是1,偏移量位1符合;int i占对齐数是4,偏移量位4刚刚好符合。都放完后,字节需要是最大对齐数的整数倍,所以就是8个字节。

4.4 练习3

struct S1
{char c1;int i;char c2;};
struct S3
{char c1;struct S1 s1;double d;
};
int main()
{printf("%zd\n", sizeof(struct S3));return 0;
}

4.3.1  分析

char c1占1一个字节,struct S1 s1上面我们已经知道了占12个字节,但为什么是偏移量为4的地方放呢?这是因为结构体S3中有S1,S1中的最大对齐位置取决于自己的最大对齐数,而S1的最大对齐数是4,所以从偏移量为4可以开始放s1;double d占8个字节,偏移量16刚刚好是8的倍数;所struct S3中最大的对齐数是12,而且字节刚刚好是24。

4.5 小结

S1和S2的变量成员是一样的,但字节大小却是不同的,所以我们再创建结构体变量的时候,尽可能的将字节较小的类型集中在一起,这样可以在一定程度上节省空间。

4.6 对齐数存在的意义

1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定 类型的数据,否则抛出硬件异常。

2. 性能原因: 数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要 作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地 址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以 ⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两 个8字节内存块中。

总体来说:结构体的内存对⻬是拿空间来换取时间的做法。

4.7 修改默认对齐数

#pragma 这个预处理指令,可以改变编译器的默认对⻬数。

我们直接看代码

#pragma pack(1)//设置默认对⻬数为1
struct S1
{char c1;int i;char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认int main()
{printf("%zd\n", sizeof(struct S1));return 0;
}

原本打印的结果是12,但这这里改了。

输出结果:

6

结构体在对⻬⽅式不合适的时候,我们可以⾃⼰更改默认对⻬数。

5.0 结构体传参 

通过上面的学习我们知道,结构体所占字节一般都是很大的,所以在函数应用过程中,往往会采用传址,传地址只需要4\8个字节,不需要开辟那么大的空间;传值浪费空间,需要拷贝,占的空=空间是比较大的。

struct S1
{char c1;int i;char c2;
}p = {.i=10};
void test(struct S1*P)
{printf("%d", P->i);
}
int main()
{test(&p);return 0;
}

如果是传值:函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下降。

结论:
结构体传参,传结构体的地址。

6.0 结构体实现位段

5.1 定义

结构体位段(bit field)是一种数据结构,在C语言中用于存储和操作内存中的位级数据。结构体位段允许程序员指定一个变量只占用指定位数的内存空间,而不是整个字节或字。这种灵活性允许在一个字节或字中存储多个不同的位级信息,从而节省内存空间。

5.2 位段声明

位段的声明和结构是类似的,有两个不同

  1. 位段的成员必须是 int 、 unsigned int 或 signed int ,在C99中位段成员的类型也可以 选择其他类型。
  2. 位段的成员名后边有⼀个冒号和⼀个数字。
//位段式结构
struct A
{int _a : 2;//2个bit位int _b : 5;//5个bit位int _c : 10;//10个bit位int _d : 30;//30个bit位
};
int main()
{printf("%zd",sizeof(struct A));return 0;
}

 有的同学可能会算2+5+10+30 = 47bit位,那么就是6个字节。是不是这样?我们来看啊看结果

输出结果:

 8

为什么会是8呢?这就与 位段内存分配有关了。

5.3 位段内存分配

  • 位段的成员可以是 int、unsigned int、signed int或者char等类型。
  • 位段的空间上是按照需要以4个字节( signed int 或者是 char 等类型 int )或者1个字节( char )的⽅式来开辟的。
  • 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。
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;

 

5.4 位段的跨平台问题 

  1. int 位段被当成有符号数还是⽆符号数是不确定的。
  2. 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会 出问题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃 剩余的位还是利⽤,这是不确定的。

总结:

跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在 

5.5 位段使⽤的注意事项 

在使用结构体位段时需要注意以下几点:

  1. 内存对齐:位字段的大小通常由编译器自动选择,为了满足对齐要求,可能会在位字段之间插入额外的填充位。因此,位字段的大小可能不等于字段成员所占的位数之和。开发者需要了解编译器对位字段进行内存对齐的规则,以确保结构体的大小和内存布局符合预期。

  2. 位字段的类型:位字段的类型可以是整型或枚举类型,但不能是浮点型、指针类型等。这是因为浮点型和指针类型的大小是可变的,无法确定应该占多少个位。

  3. 位字段的命名和长度:位字段的命名要足够清晰明确,以便其他开发者能够理解其含义。位字段的长度要根据具体需求进行选择,过长的位字段可能会造成浪费,而过短的位字段可能无法容纳所需要的数据。

  4. 位字段的操作:位字段是以位为单位进行操作的,因此在对位字段进行赋值和取值操作时,需要使用位运算符来进行操作。开发者需要熟悉位运算符的使用方法,以确保对位字段进行正确的操作。

总之,使用结构体位段时需要了解内存对齐规则,选择适当的位字段类型、命名和长度,并使用正确的位运算符进行操作。这样才能正确地使用结构体位段,并确保代码的可读性和可维护性。

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

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

相关文章

SpringBoot+Vue项目(后端项目搭建 + 添加家居)

文章目录 1.使用版本控制管理该项目1.创建远程仓库2.克隆到本地 2.后端项目环境搭建1.创建一个maven项目2.删除不必要的文件夹3.pom.xml文件引入依赖4.application.yml 配置数据源(注意,数据库名还没写)5.com/sun/furn/Application.java 编写…

python中医学习服务管理系统flask-django-php-nodejs

随着世界经济信息化、全球化的到来和互联网的飞速发展,推动了各行业的改革。若想达到安全,快捷的目的,就需要拥有信息化的组织和管理模式,建立一套合理、动态的、交互友好的、高效的中医学习服务管理系统。当前的信息管理存在工作…

JAVA后端调用OpenAI接口 实现打字机效果(SSE)

SSE SSE(Server-Sent Events,服务器发送事件)是一种基于HTTP协议的通信技术,它允许服务器持续地将数据推送给客户端,而无需客户端发起请求。这种通信方式通常用于实时性要求较高的场景,如实时更新、通知、或…

Python Flask框架 -- 模版继承

一个网站中,大部分网页的模块是重复的,比如顶部的导航栏,底部的备案信息。如果在每个页面中都重复的去写这些代码,会让项目变得臃肿,提高后期维护成本。比较好的做法是,通过模板继承,把一些重复…

STM32-Flash闪存

简介 STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程。 读写Flash的用途 1.利用程序存储器的剩余空间来保存掉电不丢失的用户数据。 2.通过在程序中…

CVE-2024-24112 XMall后台管理系统 SQL 注入漏洞分析

------作者本科毕业设计项目 基于 Spring Boot Vue 开发而成...... [Affected Component] /item/list /item/listSearch /sys/log /order/list /member/list (need time-based blind injection) /member/list/remove 项目下载地址 Exrick/xmall: 基于SOA架构的分布式…

Django日志(一)

一、概念与配置 1.1、概述 日志是程序员经常在代码中使用快速和方便的调试工具。它在调试方面比print更加的优雅和灵活 而且日志记录对于调试很有用,可以提供更多,更好的结构化,有关应用程序的状态和运行状况的信息 Django框架的日志通过python内置的logging模块实现的,可…

Elasticsearch:让你的 Elasticsearch 索引与 Python 和 Google Cloud Platform 功能保持同步

作者:来自 Elastic Garson Elasticsearch 内的索引 (index) 是你可以将数据存储在文档中的位置。 在使用索引时,如果你使用的是动态数据集,数据可能会很快变旧。 为了避免此问题,你可以创建一个 Python 脚本来更新索引&#xff0…

每日OJ题_牛客WY3 小易的升级之路(IO型OJ)

目录 牛客WY3 小易的升级之路 解析代码 牛客WY3 小易的升级之路 小易的升级之路_牛客题霸_牛客网 解析代码 #include <iostream> #include <vector> using namespace std;int getVal(int a, int b) {if (a > b)swap(a, b);for (int i a; i > 1; --i){if…

科研绘图一:箱线图(添加贝赛尔曲线)

R语言绘图系列—箱线图贝赛尔曲线 &#xff08;一&#xff09;: 科研绘图一&#xff1a;箱线图&#xff08;添加贝赛尔曲线&#xff09; 文章目录 R语言绘图系列---箱线图贝赛尔曲线&#xff08;一&#xff09;: 科研绘图一&#xff1a;箱线图&#xff08;添加贝赛尔曲线&…

【MySQL】MySQL用户管理

文章目录 一、用户1.用户信息2.创建用户3.删除用户4.修改用户密码 二、数据库的权限1.给用户授权2.回收权限 一、用户 如果我们只能使用root用户&#xff0c;这样存在安全隐患。这时&#xff0c;就需要使用MySQL的用户管理。 1.用户信息 我们安装mysql之后&#xff0c;会自动…

DC-1靶场

一.环境搭建 下载地址 http://www.five86.com/downloads/DC-1.zip 把桥接设置为nat模式&#xff0c;打开靶机的时候会提示几个错误&#xff0c;点击重试即可 启动靶机&#xff0c;如下图所示即可 二.开始打靶 1.信息收集 arp-scan -l 扫描跟kali&#xff08;攻击机&…

MySQL基础(DDL,DML,DQL)

目录 一DDL 1.1数据库操作 1.1.1查询所有数据库&#xff1a; 1.1.2创建数据库 1.1.3 使用数据库 1.1.4 删除数据库 1.2表操作 1.2.1表操作 1.2.1.1创建表 1.2.1.1.1约束 1.2.1.1.2 数据类型 1.2.1.1.2.1 数值类型 1.2.1.1.2.2 字符串类型 1.2.1.1.2.3日期类型 1.…

Linux:执行命令的命令eval与Bash解析命令的方式

相关阅读 Linuxhttps://blog.csdn.net/weixin_45791458/category_12234591.html?spm1001.2014.3001.5482 eval命令用于接收参数&#xff0c;并将这些参数作为一行命令执行&#xff0c;这也许会使人困惑&#xff0c;为什么我不能直接执行命令而需要使用eval命令间接执行呢&…

【应用笔记】LAT1305+使用STM32+TT类型IO的注意事项

1. 概述 在 STM32 系列 MCU 中&#xff0c; 除了一些特殊管脚外&#xff0c;绝大多数管脚都可以分类为 FT (兼容5V 信号)或 TT&#xff08;兼容 3V3 信号&#xff09;类型的 IO&#xff0c;由于 MCU 内部设计的不同&#xff0c; TT IO 相比 5V IO 有更多的限制&#xff0c;下面…

【回溯专题part1】【蓝桥杯备考训练】:n-皇后问题、木棒、飞机降落【已更新完成】

目录 1、n-皇后问题&#xff08;回溯模板&#xff09; 2、木棒&#xff08;《算法竞赛进阶指南》、UVA307&#xff09; 3、飞机降落&#xff08;第十四届蓝桥杯省赛C B组&#xff09; 1、n-皇后问题&#xff08;回溯模板&#xff09; n皇后问题是指将 n 个皇后放在 nn 的国…

MySQL数据库索引

目录 一.索引的基本内容 1.索引的定义 2.索引的作用 &#xff08;1&#xff09;设置了合适的索引之后&#xff0c;数据库利用各种快速定位技术&#xff0c;能够大大加快查询速度&#xff0c;这是创建索引的最主要的原因。 &#xff08;2&#xff09;当表很大或查询涉及到多…

鸿蒙一次开发,多端部署(四)工程管理

DevEco Studio的基本使用&#xff0c;请参考DevEco Studio使用指南。本章主要介绍如何使用DevEco Studio进行多设备应用开发。 说明&#xff1a; 本章的内容基于DevEco Studio 3.1.1 Release版本进行介绍&#xff0c;如您使用DevEco Studio其它版本&#xff0c;可能存在文档与产…

python --- 练习题3

目录 1、猜数字游戏&#xff08;使用random模块完成&#xff09; &#xff1a;继上期题目&#xff0c;附加 2、用户登录注册案例 3、求50~150之间的质数是那些&#xff1f; 4、打印输出标准水仙花数&#xff0c;输出这些水仙花数 5、验证:任意一个大于9的整数减去它的各位…

行业模板|DataEase制造行业大屏模板推荐

DataEase开源数据可视化分析平台于2022年6月发布模板市场&#xff08;https://templates-de.fit2cloud.com&#xff09;&#xff0c;并于2024年1月新增适用于DataEase v2版本的模板分类。模板市场旨在为DataEase用户提供专业、美观、拿来即用的大屏模板&#xff0c;方便用户根据…