前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【C语言进阶篇】看完这篇结构体文章,我向数据结构又进了一大步!(结构体进阶详解)

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

作者头像
鸽芷咕
发布于 2023-12-25 06:51:10
发布于 2023-12-25 06:51:10
88801
代码可运行
举报
文章被收录于专栏:C++干货基地C++干货基地
运行总次数:1
代码可运行

🎬 鸽芷咕个人主页 🔥 个人专栏:《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),看下有什么后果。

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

📚 代码演示:

代码语言:javascript
代码运行次数:0
运行
复制
//匿名结构体类型
struct
{
	int a;
	char b;
	float c;
}x;
struct
{
	int a;
	char b;
	float c;
}a[20], * p;q
1.2.2 匿名结构体类型的的缺陷

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

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

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

📚 代码演示:

代码语言:javascript
代码运行次数:0
运行
复制
//匿名结构体类型
struct
{
	int a;
	char b;
	float c;
}x;
struct
{
	int a;
	char b;
	float c;
}* p;
int main()
{
	p = &x;
	return 0;
}

📑 代码结果:

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

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

总结:

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

2. 结构的自引用

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

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

📚 代码演示:

代码语言:javascript
代码运行次数:0
运行
复制
//代码1
struct Node
{
 int data;
 struct Node next;
};
//可行否?

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

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

2.1 结构体自引用的作用

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

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

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

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

📚 代码演示:

代码语言:javascript
代码运行次数:0
运行
复制
//代码2
struct Node
{
 int data;
 struct Node* next;
};

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

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

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

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

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

📑 代码结果:

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

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

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

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

3.1 用标签名定义和初始化

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

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

📚 代码演示:

代码语言:javascript
代码运行次数:0
运行
复制
struct Point
{
	int x;
	int y;
}p1; //声明类型的同时定义变量p1
//全局变量
int main()
{
	struct Point p2;//直接使用标签名定义
	//局部变量
	return 0;
}

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

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

📚 代码演示:

代码语言:javascript
代码运行次数:0
运行
复制
struct Point
{
	int x;
	int y;
}p1 = { 2, 1 };//创建时直接赋值
int main()
{
	struct Point p2 = {12,13};//直接使用标签名定义
							//然后进行赋值		 
	return 0;
}
3.1.1 如何不按顺序初始化

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

  • 其实只需要用 点操作符 (.) 找到成员然后再赋值就可以了
代码语言:javascript
代码运行次数:0
运行
复制
#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 的定义结构体后面默认跟的是重命名的类型名。

📚 代码演示:

代码语言:javascript
代码运行次数:0
运行
复制
#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.结构体嵌套的定义和初始化

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

  • 既然你也是结构体那么我,用大括号在包含一下
  • 给你赋值不就完了,大括号套大括号
代码语言:javascript
代码运行次数:0
运行
复制
#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 空指针就好!

📚 代码演示:

代码语言:javascript
代码运行次数:0
运行
复制
#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. 结构体该如何传参

代码语言:javascript
代码运行次数:0
运行
复制
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() 函数进行赋值。

  • 但是我们是 传值调用 ,所以改变形参并不会改变实参。
  • 形参只是实参的一份临时拷贝
代码语言:javascript
代码运行次数:0
运行
复制
#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修饰一下指针,让指针所指向的值不能发生改变这样就可以了!

📚 代码演示:

代码语言:javascript
代码运行次数:0
运行
复制
#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;
}

📑 代码结果:

📝全篇总结

✅ 归纳: 好了以上就是关于结构体进阶篇的全部内容了,希望各位铁铁们看完也都会了呢! 结构体的自引用 自引用的例子链表 结构体嵌套如何定义 结构体传参的注意事项 ☁️ 把本章的内容全部掌握,铁汁们就又向下一部分知识数据结构前进了一大步呢! 看到这里了还不给博主扣个: ⛳️ 点赞☀️收藏 ⭐️ 关注 💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖 拜托拜托这个真的很重要! 你们的点赞就是博主更新最大的动力! 有问题可以评论或者私信呢秒回哦。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-12-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
【c语言】详解结构体
在学习每个类型之前我们需要了解其存在的意义,即什么是结构体?为什么要引入结构体这个类型呢? 我们可以想象现实中我们是如何处理一个人信息的?假设现在要汇总每个同学的个人信息,这时我们会对每个人的姓名,年龄等信息单独建表吗?显然不会,通常会给每个人发一张信息采集表,在表上可以填写每个人的姓名,年龄,体重等信息。 在c语言中像这种信息采集表形式的数据结构是通过结构体来实现的。其中姓名,年龄等信息就是结构体的成员变量,显然这些结构体的成员变量可以是不同的数据类型。 在引入结构体这个类型后,我们想表示类似学生信息这样的工作时,就会大大减少变量的创建,也减少了我们的工作量,提高了代码的可读性。
用户11029269
2024/03/19
1650
【c语言】详解结构体
如何深入掌握C语言结构体(详解)
结构体,是将不同类型的数据存放在一起,作为一个整体进行处理,其最主要的作用就是封装(可以再次利用)(作用不是简便)
用户9645905
2022/11/30
5670
当包容结构体遇见灵活的内存管理
🌈个人主页:Rookie Maker 🔥 系列专栏:c语言 🏆🏆关注博主,随时获取更多关于IT的优质内容!🏆🏆
编程交流
2024/05/11
1350
当包容结构体遇见灵活的内存管理
自定义类型:结构体
C语言已经提供了内置类型,如:char、short、int、long、float、double等,但是只有这些内置类型还是不够的,假设我想描述学生,描述一本书,这时单一的内置类型是不行的。
Crossoads
2024/10/21
1550
自定义类型:结构体
结构体(详解)
结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,结构体的声明如下:
ahao
2024/03/19
1370
结构体(详解)
【C语言初阶篇】结构体知识点的全面讲解!
🎬 鸽芷咕:个人主页 🔥 个人专栏:《C语言初阶篇》 《C语言进阶篇》
鸽芷咕
2023/12/25
3060
【C语言初阶篇】结构体知识点的全面讲解!
C语言-----自定义类型-----结构体&枚举&联合
结构体和数组一样,都是一群数据的集合,不同的是数组当中的数据是相同的类型,但是结构体中的数据类型可以不相同,结构体里的成员叫做成员变量
阑梦清川
2025/02/24
1120
C语言-----自定义类型-----结构体&枚举&联合
结构体学不明白,小代老师带你深入理解结构体
C语⾔已经提供了内置类型,如:char、short、int、long、float、double等,但是只有这些内置类 型还是不够的,假设我想描述学⽣,描述⼀本书,这时单⼀的内置类型是不⾏的。 描述⼀个学⽣需要名字、年龄、学号、⾝⾼、体重等; 描述⼀本书需要作者、出版社、定价等。C语⾔为了解决这个问题,增加了结构体这种⾃定义的数据类 型,让程序员可以⾃⼰创造适合的类型。
用户11319080
2024/10/17
1260
结构体学不明白,小代老师带你深入理解结构体
C语言自定义类型结构体与位段超详解
编译器会把上面的两个声明当成完全不同的两个类型,所以是非法的。 匿名的结构体类型,如果没有对结构体类型(使用 typedef )重命名的话,基本上只能使用一次。
fhvyxyci
2024/09/24
1170
C语言自定义类型结构体与位段超详解
C语言学习系列—>一篇带你了解结构体
为什仫错误做法是错误做法? 这里的next是同一结构体类型中的next,next中又有一个next,无限套娃,是不行的。 正确的自引用是,在结构体声明里面包含一个结构体类型的指针。
南桥
2024/01/26
1370
C语言学习系列—>一篇带你了解结构体
【C】自定义类型(一)结构体
我们知道c语言有很多内置类型如下,而对于复杂地事物,这些类型是无法进行描述的,所以c语言引入了自定义类型,让我们根据所需来设置类型:
阿伟@t
2023/10/10
2480
【C】自定义类型(一)结构体
c语言进阶部分详解(详细解析自定义类型——结构体,内存对齐,位段)
各种源码大家可以去我的github主页进行查找:Nerosts/just-a-try: 学习c语言的过程、真 (github.com)
是Nero哦
2024/01/18
1940
c语言进阶部分详解(详细解析自定义类型——结构体,内存对齐,位段)
结构体详解
上面的两个结构在声明的时候省略掉了结构体标签( tag )。这种结构体只能用一次。
用户10921393
2024/01/23
1320
详解结构体--C语言
我们平时使用的C语言类型类型主要是整数类型、浮点数类型以及指针类型,你是否想过我们该如何将一串不同类型的数据整合起来,实现封装? 事实上,C语言也提供给我们一些自定义类型,让我们可以自由的进行数据组合和使用。
用户9996207
2023/01/13
1.7K0
详解结构体--C语言
10分钟带你了解结构体
面对现实问题时,我们通常要面对的事物不是可以单纯分类为char int double之类的数据类型。就如一个学生,我们会用许多“词条”来形容他,如:名字,性别,成绩,年龄········这时候我们就可以使用自定义类型。下面我们将介绍自定义类型之一 -------结构体
薄荷冰
2024/01/22
1250
10分钟带你了解结构体
详解C语言结构体
Yui_
2024/10/16
1170
详解C语言结构体
C语言进阶—自定义类型:结构体,枚举,联合
struct Student { char name[20]; int age; char sex[5]; float score; } s1, s2, s3;//s1, s2, s3 是三个结构体变量 - 全局变量
小李很执着
2024/06/15
1330
C语言进阶—自定义类型:结构体,枚举,联合
自定义类型:结构体,枚举,联合
我们已经掌握了结构体的基本使用了。 现在我们深入讨论一个问题:计算结构体的大小。 这也是一个特别热门的考点: 结构体内存对齐
绝活蛋炒饭
2024/12/16
1410
自定义类型:结构体,枚举,联合
【C语言】指针与结构体
变量有不同的类型,整形,浮点型等。那指针也有不同的类型,要将&num(num的地址)保存到p中,我们知道p就是一个指针变量,我们给指针变量相应的类型。
平凡的人1
2022/11/15
9090
【C语言】指针与结构体
【C语言】自定义类型:结构体深入解析(一)
本小节,阿森继续和你一起学习什么是结构体?结构体类型的声明和创建,然后就是结构体的初始化,随即学习结构成员的访问操作符来更好的打印结构体的数据,当然还有匿名结构体类型,和结构的自引用。文章干货满满,接下来我们就学习一下结构体吧 😃!
学习起来吧
2024/02/29
1750
【C语言】自定义类型:结构体深入解析(一)
推荐阅读
相关推荐
【c语言】详解结构体
更多 >
目录
  • 文章目录
  • 📋 前言
  • 1 结构体的声明
    • 1.1 结构的基础知识
    • 1.2 结构的声明
      • 1.2.1 . 匿名结构体类型声明
      • 1.2.2 匿名结构体类型的的缺陷
  • 2. 结构的自引用
    • 2.1 结构体自引用的作用
    • 2.2 结构体自引用的注意事项
    • 3.结构体变量的定义和初始化
      • 3.1 用标签名定义和初始化
      • 3.2 typedef 的定义和初始化
  • 3.结构体嵌套的定义和初始化
    • 3.1 自引用的定义和初始化
  • 4. 结构体该如何传参
    • 4.1 错误的结构体传参
    • 4.2 正确的结构体传参
  • 📝全篇总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档