【C语言进阶篇】看完这篇结构体文章,我向数据结构又进了一大步!(结构体进阶详解)

news2024/11/15 8:10:14

在这里插入图片描述

🎬 鸽芷咕:个人主页

 🔥 个人专栏:《C语言初阶篇》 《C语言进阶篇》

⛺️生活的理想,就是为了理想的生活!

文章目录

  • 📋 前言
  • 1 结构体的声明
    • 1.1 结构的基础知识
    • 1.2 结构的声明
      • 1.2.1 . 匿名结构体类型声明
      • 1.2.2 匿名结构体类型的的缺陷
  • 2. 结构的自引用
    • 2.1 结构体自引用的作用
    • 2.2 结构体自引用的注意事项
    • 3.结构体变量的定义和初始化
      • 3.1 用标签名定义和初始化
        • 3.1.1 如何不按顺序初始化
      • 3.2 typedef 的定义和初始化
  • 3.结构体嵌套的定义和初始化
    • 3.1 自引用的定义和初始化
  • 4. 结构体该如何传参
    • 4.1 错误的结构体传参
    • 4.2 正确的结构体传参
  • 📝全篇总结

📋 前言

  🌈hello! 各位宝子们大家好啊,结构体的基本使用和常见错误在上一篇详细讲解过了,不知道大家都学会了没有。
  ⛳️今天给大家来个硬菜,教点高级点的结构体结构,给我们的数据结构开个好头!废话不多说直接进入正题
  📚本期文章收录在《C语言进阶篇》,大家有兴趣可以看看呐
  ⛺️ 欢迎铁汁们 ✔️ 点赞 👍 收藏 ⭐留言 📝!

1 结构体的声明

1.1 结构的基础知识

这个部分博主在前一篇博客已经详细讲解过了,一俩句话也解释不清楚,结构体这方面基础不是很好的铁铁们可以去阅读一下这篇文章哦!万字详解包看包会!

🔥 注:上一篇结构体万字解析的链接《结构体的基础讲解》

1.2 结构的声明

1.2.1 . 匿名结构体类型声明

在声明结构的时候,也可以不完全的声明。面的两个结构在声明的时候省略掉了结构体标签(tag),看下有什么后果。

  • 而这就叫匿名结构体类型

📚 代码演示:

//匿名结构体类型
struct
{
	int a;
	char b;
	float c;
}x;
struct
{
	int a;
	char b;
	float c;
}a[20], * p;q

1.2.2 匿名结构体类型的的缺陷

那么匿名结构体有什么缺陷呢?其实有俩个缺陷匿名结构体第一没有 标签名,连名字都没有所以只能在创建结构体时定义。

  • 只能在创建结构体时定义结构体变量
  • 相同类型的结构体,我们编译器认为是不一样的

假如我们有俩个相同类型的 匿名结构体 ,一个用来创建。一个创建 匿名结构体指针 用来存放相同类型的结构体变量地址!

📚 代码演示:

//匿名结构体类型
struct
{
	int a;
	char b;
	float c;
}x;
struct
{
	int a;
	char b;
	float c;
}* p;
int main()
{
	p = &x;
	return 0;
}

📑 代码结果:

这里我们就可以看出虽然都是相同类型的匿名结构体,但是在编译期间我们的编译器认为他们俩类型是不一样的。

  • 相同类型的 匿名 结构体指针,接收不了相同类型匿名结构体的地址

在这里插入图片描述
总结:

  • 匿名结构体只能在创建是定义
  • 相同类型的匿名结构体,编译器会这两个声明当成完全不同的两个类型。
  • 所以匿名结构体只适合,那种只用一次的结构体上用法很少。

2. 结构的自引用

那么结构体如果想包含一个该结构本身的成员是否可以呢?答案是可以的,这种用法我们在数据结构这门课 链表时会经常使用!

  • 那么我们看下面这段代码,自引用结构体是否可以这样定义呢?

📚 代码演示:

//代码1
struct Node
{
 int data;
 struct Node next;
};
//可行否?

如果可以,那 sizeof(struct Node)是多少?诶这里我们就会发现我们根本计算不了这个结构体的大小是多少!

  • 这里就和套娃一样,int data 我们知道是四个字节
  • struct Node 里面又包含了 struct Node 这个根本就算不了嘛!逻辑上就错误了!

2.1 结构体自引用的作用

在我们数据结构体中有一个叫做链表的数据结构,这里就不给大家详细解释了,只给大家见见猪跑!免得搞混了。链表是我们数据结构中用来指向相同类型的元素但是在不同空间里的连续存储方式。

  • 使他们向像一个链子一样可以相互链接访问

在这里插入图片描述
大家看着张图是不是就明白很多呢? 我们想在节点一找到相同类型的下一个节点:

  • 诶!那这样我们只需要把相同类型节点的地址存放到上一个节点处是不是就可以了?
  • 而不是去存放下一个节点的内容。那么我们就定义一个结构体
  • 他的成员一个负责存放数组,一个负责存放下一个地点的地址

📚 代码演示:

//代码2
struct Node
{
 int data;
 struct Node* next;
};

这样我们就可以访问一块不连续空间但是,是相同类型的结构体变量了。也可以对比数组

  • 数组是一块连续的空间里存放相同类型的数据
  • 链表是一块不连续的空间里存放不相同类型的数据
  • 而这就是结构体自引用的妙用了

2.2 结构体自引用的注意事项

但是在使用的时候,有些人会犯这样的错误一定要注意。

  • 我们知道结构体可以重命名而很多人就会把重名的结构体当成结构体成员。
  • 但这是非常错误的
//代码3
typedef struct
{
 int data;
 Node* next;
}Node;
//这样写代码,可行否?

📑 代码结果:

这时在编译期间就会发生错误,我们typedef 重定义还没生效呢!你就开始使用重定义之后的类型名了。

在这里插入图片描述

原因:
结构体重定义在结构体结束时最后一行才生效,但是我们在重定义生效之前就想使用这肯定回发生错误呢!

  • 正确的做法是在结构体里面我们还是使用未重命名之前的标签名。
//解决方案:
typedef struct Node
{
 int data;//数据域
 struct Node* next;//指针域
}Node;

3.结构体变量的定义和初始化

3.1 用标签名定义和初始化

这样的话我们可以直接在结构体后面直接定义变量,或者在需要定义的地方使用标签名定义结构体变量。

  • 在声明结构体是创建的结构体变量是 全局变量
  • 在大括号里面创建的结构体是 局部变量

📚 代码演示:

struct Point
{
	int x;
	int y;
}p1; //声明类型的同时定义变量p1
//全局变量
int main()
{
	struct Point p2;//直接使用标签名定义
	//局部变量
	return 0;
}

而初始化的话也非常简单和数组是差不多的每个成员赋值用大括号扩起来,单引号隔开就好了!

  • 大括号括起来,后面加引号
  • 每个成员逗号隔开

📚 代码演示:

struct Point
{
	int x;
	int y;
}p1 = { 2, 1 };//创建时直接赋值
int main()
{
	struct Point p2 = {12,13};//直接使用标签名定义
							//然后进行赋值		 
	return 0;
}

3.1.1 如何不按顺序初始化

有人说,那么我不想按结构体成员顺序赋值怎么办呢?

  • 其实只需要用 点操作符 (.) 找到成员然后再赋值就可以了
#include <stdio.h>
struct Stu
{
	char name[20];//姓名
	int age;//年龄
	char sex[5];//性别
	char id[20];//学号
	
}p1 = { .id="20202356",.age = 18, .name = "lisi",.sex = "nan"};
int main()
{
	printf("姓名\t年龄\t性别\t学号\n");
	printf("%s\t %d\t%s\t%s\t", 
								p1.name,
								p1.age, 
								p1.sex, 
								p1.id);
	return 0;
}

📑 代码结果:

在这里插入图片描述

3.2 typedef 的定义和初始化

typedef重命名的结构体类型,只是定义与前面不一样,其他部分都是一样的!

  • 但是重命名了,在声明后面就不能再创建我们的结构体变量了
  • 因为typedef 的定义结构体后面默认跟的是重命名的类型名。

在这里插入图片描述
📚 代码演示:

#include <stdio.h>
typedef struct Stu
{
	char name[20];//姓名
	int age;//年龄
	char sex[5];//性别
	char id[20];//学号
	
}Stu;
//全局变量
Stu p1 = { .id = "20202356",.age = 18, .name = "lisi",.sex = "nan" };
int main()
{
	printf("姓名\t年龄\t性别\t学号\n");
	//局部变量
	Stu p2 = { .id = "202329",.age = 20, .name = "zhangsan",.sex = "nan" };

	printf("%-10s\t %d\t %s\t %s\n", p1.name, p1.age, p1.sex, p1.id);
	printf("%-10s\t %d\t %s\t %s\t", p2.name, p2.age, p2.sex, p2.id);
	return 0;
}

📑 代码结果:
在这里插入图片描述

3.结构体嵌套的定义和初始化

结构体包含结构体的初始化,既然我们知道结构体是如何初始化的,那么结构体包含也就很明确了。

  • 既然你也是结构体那么我,用大括号在包含一下
  • 给你赋值不就完了,大括号套大括号
#include <stdio.h>
struct Stu
{
	char a;
	int num;	
};
struct S
{
	int arr[10];
	struct Stu sn;
	double d;

};
int main()
{
	struct S s = {{1,2,3,4,5,6,7,8,9,10},
				{"l",99},
				3.14};
	return 0;
}

3.1 自引用的定义和初始化

前面结构体包含结构体的的初始化我们都知道了,那么自引用和它基本一样,自引用引用的是相同类型的地址所以我们可以先赋值为 NULL 空指针就好!

📚 代码演示:

#include <stdio.h>
struct Point
{
	int x;
	int y;
};
struct Node
{
	int data;
	struct Point p;
	struct Node* next;
}n1 = { 10, {4,5}, NULL }; //结构体嵌套初始化
int main()
{
	struct Node n2 = { 20, {5, 6}, NULL };//结构体嵌套初始化
	printf("%d\t%d\t%d\t0x%p", 
					n2.data, 
					n2.p.x, 
					n2.p.y, 
					n2.next);
	return 0;
}

📑 代码结果:

在这里插入图片描述

4. 结构体该如何传参

struct S
{
 int data[1000];
 int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
 printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
 printf("%d\n", ps->num);
}
int main()
{
 print1(s);  //传结构体
 print2(&s); //传地址
 return 0;
}

上面的 print1 和 print2 函数哪个好些?答案是:首选print2函数。因为:

  • 函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
  • 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
  • 所以结构体传参的时候一定要传地址!

🔥 由于函数在传递参数时,如果我们传的是实参,那么形参将是实参的一份临时拷贝。如果我们传过去的结构体很大,那么形参也要开辟相同大小的空间就会照成空间的浪费!

4.1 错误的结构体传参

我们来看一下下面这个例子大家就明白了,这里我们在 main() 主函数里面创建了结构体变量想通过 test() 函数进行赋值。

  • 但是我们是 传值调用 ,所以改变形参并不会改变实参。
  • 形参只是实参的一份临时拷贝
#include <stdio.h>
typedef struct Point
{
	int x;
	int y;
}Pt;

void test(Pt pf)
{
	pf.x = 2;
	pf.y = 3;
}
int main()
{
	Pt p2 = {0};
	test(p2);
	printf("%d %d", p2.x, p2.y);
	return 0;
}

📑 代码结果:

在这里插入图片描述

4.2 正确的结构体传参

所以我们在结构体传参的时候一定要使用 传址调用 ,这样才能改变我们的结构体!

  • 如果只想使用里面的值,而不想改变结构体变量
  • 只许需要加上const修饰一下指针,让指针所指向的值不能发生改变这样就可以了!

📚 代码演示:

#include <stdio.h>
typedef struct Point
{
	int x;
	int y;
}Pt;
void test(Pt* x)
{
	x->x = 10;
	x->y = 5;
}
void test1(const Pt* x)
{
	printf("%d %d", ps.x, ps.y);
}
int main()
{
	Pt ps = { 0 };
	test(&ps);
	test1(&ps);
	printf("%d %d", ps.x, ps.y);
	return 0;
}

📑 代码结果:

在这里插入图片描述

📝全篇总结

✅ 归纳:
好了以上就是关于结构体进阶篇的全部内容了,希望各位铁铁们看完也都会了呢!
  结构体的自引用
  自引用的例子链表
  结构体嵌套如何定义
  结构体传参的注意事项
☁️ 把本章的内容全部掌握,铁汁们就又向下一部分知识数据结构前进了一大步呢!
看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注

💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖
拜托拜托这个真的很重要!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。
在这里插入图片描述

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

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

相关文章

【深度学习实践】垃圾检测

简介 本项目使用深度学习目标检测开源框架PaddleDetection中的yolox算法实现了垃圾检测&#xff0c;本文包含了从头训练yolox模型和直接使用训练好的模型进行推理的代码及相关权重。 一、数据集准备 本次训练的数据集为coco格式&#xff0c;共包含150张垃圾的照片&#xff0…

每日一题——丢失的数字

丢失的数字 题目链接 注&#xff1a;这一题的解法建立在位运算——异或^的基础之上&#xff0c;如果位运算和异或操作符不太了解&#xff0c;建议先看看&#xff1a; 位运算详解 只出现一次的数字 思路 同样&#xff0c;这题要求时间复杂度为O(n)&#xff0c;空间复杂度为O…

二叉树中的深搜

一)计算布尔二叉树的值 2331. 计算布尔二叉树的值 - 力扣&#xff08;LeetCode&#xff09; 1)计算布尔二叉树需要从叶子节点向上进行计算&#xff0c;从下向上进行计算 2)完整二叉树是同时拥有左孩子和右孩子&#xff0c;或者是完全没有右孩子 3)当我只是盯着根节点来看的时候…

JSON Web 令牌 (JWT)攻击

一、什么是JSON Web 令牌 &#xff08;JWT&#xff09; JSON Web令牌&#xff08;JWT&#xff09;是一种开放标准&#xff08;RFC 7519&#xff09;&#xff0c;用于在网络应用间传递声明信息。它是一种轻量级、自包含的安全性传输格式&#xff0c;通常用于在身份验证和授权过程…

三更博客系统(完整笔记+前后台系统代码实现)

三更博客前后端分离系统 前后端分离博客系统1.技术栈2.创建工程3.博客前台3.0 准备工作3.1 SpringBoot和MybatisPuls整合配置测试 3.1 热门文章列表3.1.0 文章表分析3.1.1 需求3.1.2 接口设计3.1.3 基础版本代码实现3.1.4 使用VO优化3.1.5 字面值处理 3.2 Bean拷贝工具类封装3.…

C#百万数据处理

C#百万数据处理 在我们经验的不断增长中不可避免的会遇到一些数据量很大操作也复杂的业务 这种情况我们如何取优化如何去处理呢&#xff1f;一般都要根据业务逻辑和背景去进行合理的改进。 文章目录 C#百万数据处理前言一、项目业务需求和开发背景项目开发背景数据量计算业务需…

OpenFeign原理浅析

OpenFeign原理我个人觉得是非常简单的&#xff0c;如果你对Spring非常了解&#xff0c;知道FactoryBean&#xff0c;以及注入bean的方式&#xff0c;并掌握动态代理&#xff0c;那么自己实现一个类似这样的Http代理客户端是一点问题也没有的&#xff01; 使用流程 首先我们先过…

BLE连接、配对和绑定

参考&#xff1a;一篇文章带你解读蓝牙配对绑定 参考&#xff1a;BLE安全之SM剖析(1) 参考&#xff1a;BLE安全之SM剖析&#xff08;2&#xff09; 参考&#xff1a;BLE安全之SM剖析(3) 参考&#xff1a;https://blog.csdn.net/chengbaojin/article/details/103691046 参考&…

【MQTT5】原生PHP对接Uni H5、APP、微信小程序实时通讯消息服务

文章目录 视频演示效果前言一、分析二、全局注入MQTT连接1.引入库2.写入全局连接代码 二、PHP环境建立总结 视频演示效果 【uniapp】实现买定离手小游戏 前言 Mqtt不同环境问题太多&#xff0c;新手可以看下 《【MQTT】Esp32数据上传采集&#xff1a;最新mqtt插件&#xff08;支…

Flowable-服务-骆驼任务

目录 定义图形标记XML内容Flowable与Camel集成使用示例设计Came路由代码 定义 Camel 任务不是 BPMN 2.0 规范定义的官方任务&#xff0c;在 Flowable 中&#xff0c;Camel 任务是作为一种特殊的服务 任务来实现的。主要做路由工作的。 图形标记 由于 Camel 任务不是 BPMN 2.…

BMI指数计算小工具Java

现在越来越多的人关注健康&#xff0c;关注身材管理&#xff0c;不妨做个小工具&#xff0c;计算自己的BMI&#xff0c;给自己制定合理的健身或减肥计划&#xff0c;享受健康生活&#xff01;&#xff01;&#xff01;BMI的计算标准从网上找的&#xff0c;不知道是否准确&#…

❤ yarn 和npm 的使用

❤ yarn 和npm 的使用 yarn 版本1的使用 yarn 简介 Yarn是facebook发布的一款取代npm的包管理工具。 yarn特点&#xff1a; 1&#xff0c;速度超快。 Yarn 缓存了每个下载过的包&#xff0c;所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率&#xff0c;因…

TransGPT 开源交通大模型开源

TransGPT 是开源交通大模型&#xff0c;主要致力于在真实交通行业中发挥实际价值。 它能够实现交通情况预测、智能咨询助手、公共交通服务、交通规划设计、交通安全教育、协助管理、交通事故报告和分析、自动驾驶辅助系统等功能。 TransGPT 作为一个通用常识交通大模型&#…

cmd相关操作命令

1.根据端口号查询对应进程的PID netstat -ano | findstr 端口号 例如&#xff1a;netstat -ano | findstr 9080&#xff1b;该端口所属进程的PID为6684 2.根据PID查询对应进程 tasklist | findstr PID 例如&#xff1a;tasklist | findstr 6684&#xff1b;该PID所属进程名为…

啥都收费,不仅智能电视没人买了,连电视盒子也卖不出了

分析机构给出的数据指今年上半年国内的电视盒子销量跌破百万至92.9万台&#xff0c;同比下滑29%&#xff0c;享受额更是大跌32%&#xff0c;显示出电视盒子即使大幅降价也没人买了&#xff0c;导致消费者远离电视在于收费太离谱了。 一、啥都收费 如今的智能电视其实并不智能&a…

使用贝叶斯滤波器通过运动模型和嘈杂的墙壁传感器定位机器人研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Day48 算法记录|动态规划15 (子序列)

子序列 392. 判断子序列115.不同的子序列 392. 判断子序列 这道题和1143最长公共字串相同 dp[i][j] 表示以下标i-1为结尾的字符串s&#xff0c;和以下标j-1为结尾的字符串t&#xff0c;相同子序列的长度为dp[i][j]。 class Solution {public boolean isSubsequence(String s,…

【C语言初阶篇】自定义类型结构体我不允许还有人不会!

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 &#x1f4cb; 前言1 . 什么是结构体1.1 结构的定义1.2 结构的声明 2.结构体初始化2.1 用标签名定义和初始化2.2…

【Vue】Vue-Cli整合Echart

Vue-Cli整合Echart 文章目录 Vue-Cli整合Echart一、创建Vue-Cli项目1、创建并运行Vue Cli hello-world项目2、运行vue-element-admin项目 二、Vue-Cli整合Echart1、使用echarts CDN源实现K线图绘制2、使用VueCli echarts实现K线图绘制3、echart更多使用问题整理 一、创建Vue-C…

LeetCode222. 完全二叉树的节点个数

222. 完全二叉树的节点个数 文章目录 [222. 完全二叉树的节点个数](https://leetcode.cn/problems/count-complete-tree-nodes/)一、题目二、题解方法一&#xff1a;递归遍历所有结点方法二&#xff1a;根据完全二叉树的特性递归方法三&#xff1a;迭代 一、题目 给你一棵 完全…