6. 结构体和共用体
结构体
结构体 struct
是 C 语言中的一种特殊变量类型,它能将不同类型的数据组合成一个整体:
// 定义一个名为 Data 的结构体类型
struct info {
int a;
char b;
float c;
};
struct info data = {1, 'a', 3.14f}; // 定义并初始化一个 Data 类型的变量 data
提示
你也可以不给结构体名字,它将作为匿名结构体,你没法再在别处使用它,你只能在创建的时候立刻使用它:
struct {
int data1;
char data2;
double data3;
} data;
data.data1 = 1;
// ...
提示
配合 typedef
语法,你可以通过创建类型别名省去结构体定义时所需要写的 struct
:
typedef struct {
// ...
} data_t;
// 不需要写 struct
data_t data;
提示
结构体变量类型的命名规则也是使用 snake_case
命名法,即小写字母和下划线组合,并且首字母小写。
但如果你使用了 typedef
语法创建了别名,你应当在后面加上 _t
后缀,以使表达更加清楚。
那么,结构体可以储存变量我明白了,那我如果想把函数也打包进来,作为这个数据类型所创建的变量能够使用的方法,有没有办法呢?
有的,兄弟,有的,恰恰是这种方法,能够让 C 语言这个不是面向对象的语言变得面向对象起来!
提示
什么是对象?
对象是面向对象编程(OOP)中的一个基本概念,它是一个包含数据和行为的实体。对象可以看作是现实世界中的事物或概念的抽象,它具有状态和行为。在面向对象编程中,对象是程序的基本单元,它封装了数据和操作数据的方法,并且可以与其他对象进行交互。
对象通常由两个部分组成:
- 属性(属性):对象的属性是对象所具有的特性和特征,它描述了对象的状态。例如,一个人的属性可能包括姓名、年龄、性别等。
- 方法(方法):对象的方法是对象所具有的行为和操作,它描述了对象的行为。例如,一个人的方法可能包括行走、说话、吃饭等。
但因为 C 语言不能在初始化时自动指定变量的初始值,因此我们建议自行指定结构体的创建和删除方法,并全程使用创建出来的指针访问这个变量:
提示
如果是专门为某个结构体设计的函数,我们建议使用 this
作为第一个参数,以表示这个函数所操作的对象。
另外,这个函数的名称应当加上这个结构体的名称作为前缀,以标注这个供给该结构体使用的函数。
typedef struct _cat cat_t
struct _cat {
int age;
const char *name;
void (*set_age)(cat_t *, int);
void (*speak)(cat_t *);
};
void cat_set_age(cat_t *this, int age) {
this->age = age;
}
void cat_speak(cat_t *this) {
printf("%s: Meow~\n", this->name);
}
cat_t *create_cat(int age, char *name) {
cat_t *cat = malloc(sizeof(cat_t));
cat->age = age;
// 字符串需要一个 '\0' 字符作为结尾标识,因此需要多分配一个字节的空间
cat->name = malloc(sizeof(char) * (strlen(name) + 1));
strcpy(cat->name, name);
cat->set_age = cat_set_age;
cat->speak = cat_speak;
}
// 注意,这里需要传入指针的指针,因为我们在释放后需要修改原指针的值,以防止悬空指针
void destroy_cat(cat_t **cat) {
free((*cat)->name);
// 将已经释放的指针置为 NULL,防止悬空指针,这看似无用,但是是一种好习惯
(*cat)->name = NULL;
free(*cat);
*cat = NULL;
}
int main() {
// 完整使用小猫
cat_t *cat = create_cat(1, "Tom");
cat->set_age(cat, 2);
cat->speak(cat);
destroy_cat(cat);
printf("%p\n", cat); // 输出 NULL
return 0;
}
提示
什么是悬空指针?
悬空指针(Dangling Pointer)是指向已释放内存的指针。当一个指针指向的内存被释放后,该指针仍然保留对已释放内存的引用,此时该指针就变成了悬空指针。悬空指针可能导致程序崩溃或产生不可预测的行为,因此在使用指针时需要特别注意避免悬空指针。