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

这篇具有很好参考价值的文章主要介绍了【C语言进阶篇】看完这篇结构体文章,我向数据结构又进了一大步!(结构体进阶详解)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


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

🎬 鸽芷咕:个人主页

 🔥 个人专栏:《C语言初阶篇》 《C语言进阶篇》
⛺️生活的理想,就是为了理想的生活!

📋 前言

  🌈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;
}

📑 代码结果:

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

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

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

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

2. 结构的自引用

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

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

📚 代码演示:

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

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

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

2.1 结构体自引用的作用

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

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

【C语言进阶篇】看完这篇结构体文章,我向数据结构又进了一大步!(结构体进阶详解),《C语言进阶篇》,数据结构,开发语言,C语言,C++
大家看着张图是不是就明白很多呢? 我们想在节点一找到相同类型的下一个节点:

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

📚 代码演示:

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

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

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

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

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

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

📑 代码结果:

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

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

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

  • 正确的做法是在结构体里面我们还是使用未重命名之前的标签名。
//解决方案:
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;
}

📑 代码结果:

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

3.2 typedef 的定义和初始化

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

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

【C语言进阶篇】看完这篇结构体文章,我向数据结构又进了一大步!(结构体进阶详解),《C语言进阶篇》,数据结构,开发语言,C语言,C++
📚 代码演示:

#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;
}

📑 代码结果:
【C语言进阶篇】看完这篇结构体文章,我向数据结构又进了一大步!(结构体进阶详解),《C语言进阶篇》,数据结构,开发语言,C语言,C++

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;
}

📑 代码结果:

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

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;
}

📑 代码结果:

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

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;
}

📑 代码结果:

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

📝全篇总结

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

💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖
拜托拜托这个真的很重要!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。
【C语言进阶篇】看完这篇结构体文章,我向数据结构又进了一大步!(结构体进阶详解),《C语言进阶篇》,数据结构,开发语言,C语言,C++文章来源地址https://www.toymoban.com/news/detail-619215.html

到了这里,关于【C语言进阶篇】看完这篇结构体文章,我向数据结构又进了一大步!(结构体进阶详解)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【MySQL】看完这篇不信你学不会如何使用数据处理函数

    🎬 博客主页:博主链接 🎥 本文由 M malloc 原创,首发于 CSDN🙉 🎄 学习专栏推荐:LeetCode刷题集! 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📆 未来很长,值得我们全力奔赴更美好的生活✨ 😁大家好呀,今天是我第N次写MySQL,也是最近才学习MySQL,也想着记录

    2024年02月11日
    浏览(34)
  • 删除icloud照片手机照片会不见吗?看完这篇你会找到答案

    分两种情况看, 删除iCloud照片手机照片会看不见吗? 这主要看您的iCloud中的设置而定。第一,如果您的iCloud是关闭状态,删掉以前共享到iCloud中的照片的话,手机上的照片还是存在的;第二,如果您的iCloud一直都是共享状态,则手机照片跟iCloud照片是同步的,也就是删除i

    2024年02月06日
    浏览(36)
  • ES是什么?看完这篇就不要再问这种低级问题了!

    ES的核心概念 1. 节点(Node)一个运行中的 Elasticsearch 实例称为一个节点,而集群是由一个或者多个拥有相同cluster.name配置的节点组成, 它们共同承担数据和负载的压力。ES集群中的节点有三种不同的类型,一个节点可以充当一个或多个角色,默认三个角色都有: 主节点:负责

    2024年04月17日
    浏览(39)
  • 看完这篇 教你玩转渗透测试靶机vulnhub——My File Server: 2

    vulnhub是个提供各种漏洞平台的综合靶场,可供下载多种虚拟机进行下载,本地VM打开即可,像做游戏一样去完成渗透测试、提权、漏洞利用、代码审计等等有趣的实战。 这是一个漏洞靶机,老样子需要找到flag即可。 官方下载:https://download.vulnhub.com/myfileserver/My_file_server_2.o

    2023年04月19日
    浏览(37)
  • 面试官问 : ArrayList 不是线程安全的,为什么 ?(看完这篇,以后反问面试官)

    金三银四 ? 也许,但是。 近日,又收到金三银四一线作战小队成员反馈的战况 : 我不管你从哪里看的面经,但是我不允许你看到我这篇文章之后,还不清楚这个面试问题。 本篇内容预告:   ArrayList 是线程不安全的, 为什么 ? ① 结合代码去探一探所谓的不安全  ② 我们

    2024年02月02日
    浏览(47)
  • ai图片合成要如何操作?看完这篇就知道ai绘画怎么弄了

    看看这些图片,是不是很想拿去当头像啊!你们知道这些照片是怎么做的吗?想自己创作出这么好看的头像吗?你听说过ai绘画技术吗?今天我就为大家介绍ai绘画技术是什么,ai图片合成怎么做。话不多说,直接进入正题吧! 合成方法一:利用starryai合成头像 它是一款国外的

    2024年02月14日
    浏览(31)
  • 2023最新网络安全面试题大全,看完这篇你的秋招offer就到手了!

    随着国家政策的扶持,网络安全行业也越来越为大众所熟知,想要进入到网络安全行业的人也越来越多。 为了拿到心仪的 Offer 之外,除了学好网络安全知识以外,还要应对好企业的面试。 作为一个安全老鸟,工作这么多年,面试过很多人也出过很多面试题目,也在网上收集

    2024年02月07日
    浏览(42)
  • 【C++】五一假期,我学会了C++类和对象 ( 看完这篇你也能学会 ~ )

      🧑‍🎓 个人主页:简 料   🏆 所属专栏:C++   🏆 个人社区:越努力越幸运社区   🏆 简       介: 简料简料,简单有料~在校大学生一枚,专注C/C++/GO的干货分享,立志成为您的好帮手 ~ C/C++学习路线 (点击解锁) ❤️ C语言 ❤️ 初阶数据结构与算法 ❤️ C++ ❤️

    2024年02月03日
    浏览(43)
  • 看完这篇都有DBA级别的MySQL备份经验了,再也不害怕删库跑路了

            在做运维的路上,相信大家会跟数据库经常打交道吧,那么数据库最重要的时候什么呢,那么肯定是备份了,这篇文章主要内容是介绍了,MySQL的几种常见的备份方式,主要是分为两种,第一种是基于MySQL提供的工具来进行备份,还有一种是基于使用第三方工具来进行

    2024年02月05日
    浏览(34)
  • 【Kotlin】从Java转向Kotlin,耐心看完这篇博客就够了。Kotlin快速入门教程分享

    适合群体:Java已经入门的人,如果是零基础,不要勉强!虽然没有深奥的术语,即使有也尽可能通俗易懂 。 Kotlin和Java都是Jvm语言,相同的部分能省则省(篇幅有限),重点是Kotlin。 示例代码的注释很重要。最好可以使用IDEA等开发工具运行一下。 最后创作不易,全部都是自

    2023年04月08日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包