这篇文章我们来讲讲所谓的自定义类型。
自定义类型:结构体,枚举,联合
首先我们先来看结构体。可以说,结构体这块知识你学通透了,后面的枚举和联合将不攻自破了。
结构体的声明
先来回顾一下结构的基础知识:
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
结构的声明
struct tag
{
member-list;
}variable-list;
在C语言中,结构体是一种用户自定义的数据类型,它允许将不同类型的数据组合在一起。结构体的声明通常包括定义结构体标签(可选)、结构体关键字struct
、结构体名(如果需要的话)以及一系列成员变量。
例如描述一个学生:
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};//分号不能丢
注意:分号不能丢!
除了这种最常见的写法,我们还可以使用typedef简化结构体声明
什么意思呢?
使用 typedef 可以简化结构体的声明,使得在后续代码中使用结构体时更加方便。通过 typedef,你可以为结构体创建一个新的类型别名,这样就不需要每次都写 struct 关键字了。
typedef struct {
char name[50];
int age;
float gpa;
} Student;
如果是常规写法
struct Student student1;
struct Student *student2 = malloc(sizeof(struct Student));
// 现在可以直接使用 Student 作为类型名称
Student student1;
Student *student2 = malloc(sizeof(Student));
这样就会更加方便,万一后面发现student重复,需要区分的时候,改名字也会更加方便。
特殊的声明
在声明结构的时候,可以不完全的声明。
比如
//匿名结构体类型
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;
上面的两个结构在声明的时候省略掉了结构体标签(tag)。
那么问题来了,在上面代码的基础上,下面的代码合法吗?
p = &x;
这里看代码可以知道这里定义了两个匿名结构体,并且尝试将一个指向其中一个结构体的指针赋值给另一个结构体的地址。
这样对吗?
实际上尽管这两个匿名结构体具有相同的成员(int a, char b, float c),但它们实际上是不同的类型。每个匿名结构体都是独立的类型,即使它们的成员相同。
怎么改进呢?
就要确保p的类型和x的类型相同。
方法 1:使用同一个匿名结构体类型
struct {
int a;
char b;
float c;
} x, a[20], *p;
p = &x; // 合法
上面提到类型不同,不放在同一个类型,重新定义一个类型,不就好了?大家都是同一起跑线就好了不是吗?所以
方法 2:使用 typedef 定义一个新的类型
typedef struct {
int a;
char b;
float c;
} MyStruct;
MyStruct x;
MyStruct a[20];
MyStruct *p;
p = &x; // 合法
在这种情况下,x、a 和 p 都是 MyStruct 类型的实例或指针,因此 p = &x 是合法的。
结构的自引用
在结构中包含一个类型为该结构本身的成员是否可以呢?
//代码1
struct Node
{
int data;
struct Node next;
};
//可行否?
显然是不可以的。
这种定义会导致无限递归的结构体嵌套,因为每个 Node 都会包含另一个 Node(这个struct node是完整的),依此类推,最终导致编译器无法处理这种情况。
正确的应该是
/代码2
struct Node
{
int data;
struct Node* next;
};
这里还需要注意一个细节:
//代码3
typedef struct
{
int data
Node* next;
}Node;
//这样写代码,可行否?
代码3中的定义是不可行的,因为你在结构体内部使用了 Node* next;
时,Node
还没有被完全定义。在C语言中,你不能在结构体定义的过程中直接使用该结构体类型的指针,除非你先声明该类型。
正确的应该怎么搞呢
typedef struct Node
{
int data;
struct Node* next;
}Node;
为什么注意就正确了呢?
因为在结构体内部使用 struct Node*
时,编译器只需要知道 struct Node
是一个类型,而不需要知道它的完整定义。这是因为指针只是存储一个地址,而不是整个结构体的内容。
在 typedef 语句中,struct Node 被完全定义了。因此,当编译器遇到 struct Node* next; 时,它已经知道 struct Node 是什么,所以这个定义是有效的。
感谢观看。朋友们我们下篇再见!