C语言学习
结构体基础
友情链接:C语言专栏
文章目录
- C语言学习
- 前言:
- 一、结构体类型的声明
-
- [1.1 结构的基础知识](#1.1 结构的基础知识)
- [1.2 结构的声明](#1.2 结构的声明)
- [1.3 特殊的声明](#1.3 特殊的声明)
- [1.4 结构的自引用](#1.4 结构的自引用)
- [1.5 结构成员的类型](#1.5 结构成员的类型)
- [1.6 结构体变量的定义和初始化](#1.6 结构体变量的定义和初始化)
- 二、结构体成员的访问
- 三、结构体传参
- 总结
- 附录
前言:
结构体是C语言中组织复杂数据的利器。本文将用最精炼的方式,带你快速掌握结构体的核心用法:从基础声明到自引用技巧,从成员访问到传参优化。让你彻底玩转结构体编程!
一、结构体类型的声明
1.1 结构的基础知识
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
1.2 结构的声明
**struct解释:**在C语言中,struct
是一个关键字,用于声明和定义结构体类型。
结构体描述学生:
c
struct student
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
int id[20];//学号
};
咱们还可以利用关键字typedef
,对这个类型重命名,创建更简洁的类型名,代码实现:
c
typedef struct student
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
int id[20];//学号
}Stu;
可知:此时struct student
与Stu
等效,这两种声明方式是等价的,都会创建相同类型的结构体变量。
1.3 特殊的声明
在声明结构的时候,可以不完全的声明,即结构在声明的时候省略掉了结构体标签(tag)。
c
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], * p;
那么问题来了,咱们这样写的话 p = &x
可以吗?
答案是不行,解释:
编译器会把上面的两个声明当成完全不同的两个类型,所以是非法的。
1.4 结构的自引用
**结构的自引用解释:**在结构中包含一个类型为该结构本身的成员。
看代码:
c
struct Node
{
int data;
struct Node next;
};
这种写法是否可行?
如果可以,那sizeof(struct Node)是多少?很显然,它会无限递归。
禁止直接嵌套:结构体不能包含自身类型的完整对象(会导致无限递归)。
那么咱们应该怎么来嵌套呢,指针,指针在固定的机器下是固定的。所以咱们可以这样来自引用:
c
struct Node
{
int data;
struct Node* next;//利用指针来实现自引用
}
咱们再来看个代码:
c
//是否可行?
typedef struct
{
int data;
Node* next;
}Node;
答案是不行,会报错,解释:
类型定义的顺序问题,
在typedef完成之前(typedef在遇到分号
;
时才算真正完成),Node这个名字还不存在。但在结构体内部,已经尝试使用Node*,此时编译器会报错。
所以咱们应该怎么写呢?看正确代码:
c
typedef struct Node
{
int data;
struct Node* next; // 使用"struct Node"引用自身
} Node; // typedef别名
1.5 结构成员的类型
结构的成员可以是标量、数组、指针,甚至是其他结构体。
1.6 结构体变量的定义和初始化
有了结构体类型,那如何定义变量,其实很简单。咱们以前的int、char、......怎么定义,结构体变量就怎么定义。
咱们还要辨析一个概念,结构体类型与咱们已知的int、char、float......都是相似的,只不过结构体类型是咱们自己根据需求来自己定义的类型而已。即结构体是用户根据需求自定义的复合类型 。
那咱们来看看结构体变量的定义和初始化:
c
//1. 声明类型的同时定义变量p1
struct Point
{
int x;
int y;
}p1;
//2. 单独定义结构体变量p2
struct Point p2;
//初始化:以上两种定义变量同时都可以赋初值(初始化)。
struct Point p3 = { 1, 2 };
//结构体2声明
struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
struct Stu s = { "zhangsan", 20 };//初始化
//结构体也可以嵌套定义和初始化
struct Node
{
int data;
struct Point p;//结构体嵌套定义
}n1 = { 10, {4,5} }; //结构体嵌套初始化
struct Node n2 = { 20, {5, 6} };//结构体嵌套初始化
二、结构体成员的访问
咱们现在已知结构的声明、结构体的定义......那么怎么修改、访问结构体成员呢?
这里主要涉及到两个操作符.
->
,咱们来看看怎么用呢?
结构体变量访问成员
结构变量的成员是通过点操作符(.)访问的。点操作符接受两个操作数。
看代码:
c
typedef struct Stu
{
char name[20];
int age;
}Stu;
int main()
{
Stu s;//定义结构体变量
strcpy(s.name, "zhangsan");//使用.访问name成员
//注意:这里不能写为s.name = "zhangsan"
s.age =18;//使用.访问age成员
return 0;
}
看下调试结果:
结构体指针访问指向变量的成员
有时候我们得到的不是一个结构体变量,而是指向一个结构体的指针。
那该如何访问成员。
看代码:
c
typedef struct Stu
{
char name[20];
int age;
}Stu;
void print(Stu* ps)
{
//结构体变量直接访问成员
printf("name = %s age = %d\n", (*ps).name, (*ps).age);
//使用结构体指针访问指向对象的成员
printf("name = %s age = %d\n", ps->name, ps->age);
}
int main()
{
Stu s = { "zhangsan", 18 };
print(&s);//结构体地址传参
return 0;
}
输出:
三、结构体传参
直接看代码:
c
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
函数。
原因:
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的
下降。
总结:
结构体传参的时候,要传结构体的地址。
总结
希望本文能帮助你建立扎实的结构体知识体系,为后续的C语言开发打下坚实基础。如有任何疑问,欢迎在评论区交流讨论!