在C语言中,除了基本类型,我们还能基于它们构造出更复杂的数据形态,这被称为**派生数据类型。**408考试的核心就三类:数组、结构体、指针。
注:联合体(共用体)在考纲中常与结构体对比考察,枚举则涉及较少,这里一并讲解以形成完整体系。
一、数组(Array)
本质:一组相同类型元素的集合,在内存中连续存放。
1.定义与初始化
int a[5]; // 未初始化,值为随机
int b[5] = {1, 2, 3}; // 部分初始化,剩余补0:{1,2,3,0,0}
int c[] = {1, 2, 3}; // 长度自动为3
char str[] = "hello"; // 字符串,长度为6(含'\0')
2.一维与二维数组(重点:地址映射)
408喜欢考二维数组的存储方式(行优先)和某个元素地址的计算。
指向行的指针:
int a[2][3];
int (*p)[3] = a; // p是指针,指向"包含3个int的一维数组"
// a[i][j] 等价于 *(*(a+i)+j)
3.数组与指针(408重灾区)
数组名是地址常量,多数情况下退化为指向首元素的指针。
二、结构体(Structure)
本质:把不同类型的数据打包成一个整体,成员在内存中顺序存放(但非紧密)。
1.定义与使用
struct Student {
char name[20];
int id;
float score;
}; // 分号勿漏
struct Student stu1 = {"Zhang", 101, 95.5};
// 成员访问:stu1.id 或 指针->成员
2.内存对齐
对齐规则:
1.成员对齐:每个成员的起始偏移量,必须能整除自身大小(或其自身对齐值,取较小者)。
2.结构体总长:必须整除最宽成员的大小(整个结构体的对齐值)。
3.嵌套:内部结构体按其最宽成员的对齐值来对齐。
struct S {
char a; // 1字节
int b; // 4字节
short c; // 2字节
};
// 分析(假设4字节对齐):
// a放在偏移0,b要从偏移4开始,c放在偏移8。
// 总长目前是10,但要整除最宽成员int(4),故总长为12。sizeof(struct S) = 12。
优化:想省空间,把内存大的成员放前面,减少内部填充。
三、指针(Pointer)
本质:存放另一个变量的内存地址的变量。指针是C语言的灵魂,也是408难点。
1.核心概念强化
指针变量:int *p = &a ------*在定义时表示类型,在执行时表示解引用。
解引用与野指针:未初始化或已释放内存的指针。访问它们是未定义行为。是题目的常见错误点。
2.多级指针
int a = 10;
int *p = &a;
int **pp = &p; // pp是指向指针p的指针
// **pp 的值是10
四、联合体(Union)
本质:所有成员共享同一块内存。
与结构体的区别
结构体:各成员独占空间,大小=各成员之和(考虑对齐)。
联合体:大小等于其最宽成员的大小。任一时刻只能存储一个成员的值。
五、枚举(Enum)
本质:给一组整数常量取个好几的名字
enum Weekday { Mon = 1, Tue, Wed, ... }; // Tue=2, Wed=3...
enum Bool { FALSE, TRUE }; // FALSE=0, TRUE=1
考点:默认从0开始递增,可手动指定。常配合switch使用,增加代码可读性,但空间仍为int大小。
六、类型定义(Typedef)
本质:给已有类型取别名,不是创建新类型。在结构体和指针定义中特别能简化代码。
typedef struct Student {
char name[20];
int id;
} Stu; // Stu现在是struct Student的别名
typedef int (*FuncPtr)(int, int); // FuncPtr是函数指针类型
FuncPtr pf = add; // 不用再写复杂的声明了