写代码这事,说白了就是把数据组织起来,然后告诉计算机怎么处理。可数据这东西,有时候简单,有时候复杂得让人头疼。
刚开始学编程的时候,应该觉得数组挺好用。一排一样的东西,挨个排好,用下标访问,多简单。后来才发现,现实世界哪有这么规整。
数组:整齐划一的队伍
数组是C语言里最简单的数据结构。你声明一个数组,里面的元素必须都是同一个类型。
#include <stdio.h>
int main() {
int scores[5] = {85, 90, 78, 92, 88};
float prices[3] = {12.5, 8.9, 15.0};
char name[20] = "Zhang San";
printf("第一个成绩: %d\n", scores[0]);
printf("第二个价格: %.1f\n", prices[1]);
return 0;
}
数组的好处是简单直接。你告诉编译器你要几个元素,什么类型,它就给你分配一块连续的内存。访问的时候,用下标就行,快得很。
可问题也在这。数组里的元素必须都一样。你想在一个数组里既存整数又存浮点数?不行。你想存一个学生的信息,有名字、有成绩、有年龄?数组干不了这事。
现实世界的数据哪有这么整齐。一个人的信息,名字是字符串,成绩是整数,身高是浮点数。这些东西类型不一样,可它们都属于同一个人。
枚举:给数字起个名字
在说结构体之前,咱们先看看枚举。这东西其实挺简单,就是给整数起个名字,让代码好读一点。
#include <stdio.h>
// 定义枚举类型
enum Weekday {
MONDAY, // 0
TUESDAY, // 1
WEDNESDAY, // 2
THURSDAY, // 3
FRIDAY, // 4
SATURDAY, // 5
SUNDAY // 6
};
enum Status {
SUCCESS = 0,
FAILURE = -1,
TIMEOUT = -2
};
int main() {
enum Weekday today = WEDNESDAY;
enum Status result = SUCCESS;
printf("今天是星期几: %d\n", today);
printf("操作结果: %d\n", result);
if (today == WEDNESDAY) {
printf("周三了,快周末了\n");
}
return 0;
}
枚举的本质就是整数。编译器在编译的时候,会把 MONDAY 换成 0,TUESDAY 换成 1,依此类推。它不占额外的内存,也不增加运行时的开销。
那为啥要用枚举?因为人记名字比记数字容易。你写 if (status == SUCCESS) 比写 if (status == 0) 好懂多了。代码是写给人看的,顺便让机器执行。
枚举还有个好处,编译器能帮你检查类型。如果你把 Weekday 类型的变量赋值给 Status 类型,编译器会警告你。这能避免一些低级的错误。
结构体:把不同类型的数据绑在一起
结构体才是真正解决复杂数据的东西。它允许你把不同类型的数据组合在一起,形成一个新的类型。
#include <stdio.h>
#include <string.h>
struct Student {
char name[20];
int age;
float score;
char gender;
};
int main() {
struct Student stu1;
// 给结构体成员赋值
strcpy(stu1.name, "Zhang San");
stu1.age = 20;
stu1.score = 85.5;
stu1.gender = 'M';
// 访问结构体成员
printf("姓名: %s\n", stu1.name);
printf("年龄: %d\n", stu1.age);
printf("成绩: %.1f\n", stu1.score);
// 也可以直接初始化
struct Student stu2 = {"Li Si", 21, 90.0, 'F'};
printf("\n第二个学生: %s, %d岁, %.1f分\n",
stu2.name, stu2.age, stu2.score);
return 0;
}
结构体在内存里是怎么存的?就是把各个成员挨个排好。name 占20字节,age 占4字节,score 占4字节,gender 占1字节。加起来是29字节,但编译器可能会做内存对齐,实际占32字节。
内存对齐这事,说白了就是为了让CPU访问更快。CPU读内存的时候,一次读4字节或8字节最方便。如果数据没对齐,CPU可能得读两次,慢得很。
结构体指针也挺常用。
struct Student *ptr = &stu1;
// 用指针访问成员
printf("姓名: %s\n", ptr->name);
printf("年龄: %d\n", ptr->age);
// 等价于 (*ptr).name,但 -> 更简洁
-> 这个操作符就是用来通过指针访问结构体成员的。它等价于 (*ptr).name,但写起来方便多了。
共用体:省内存的妙招
共用体跟结构体长得挺像,但用法完全不一样。结构体的所有成员都有自己的内存,共用体的所有成员共享同一块内存。
#include <stdio.h>
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
printf("共用体大小: %zu 字节\n", sizeof(data));
data.i = 10;
printf("data.i = %d\n", data.i);
data.f = 220.5;
printf("data.f = %.1f\n", data.f);
// 这时候 data.i 的值已经变了,因为共享内存
strcpy(data.str, "Hello");
printf("data.str = %s\n", data.str);
// 这时候 data.i 和 data.f 都变了
return 0;
}
共用体的大小等于它最大的成员的大小。上面的例子里,str 占20字节,所以整个共用体就是20字节。不管你存的是整数还是浮点数,都在这20字节里。
共用体有什么用?省内存。当你同一时间只需要存一种类型的数据时,用共用体可以节省空间。比如网络协议里,不同类型的消息可能用同一个结构,但每次只发送一种。
不过共用体也挺危险的。你存了整数,又当浮点数读,结果完全不可预料。这东西得小心用,知道自己在干什么才行。
预览时标签不可点