C语言期末备考突击复习笔记
这份笔记包括了C语言基础学习的一些难点,而不是从基本语法开始写起,
1. 数组与指针
核心概念
-
数组名 = 首地址
int a[5];,a的值等于&a[0]。- 区别:在 Java 中数组是对象,在 C 中数组是连续内存块。
-
指针运算(跳格子)
-
指针加减不是简单的数学加减,而是基于数据类型的大小(步长)。
-
假设地址为 1000:
-
c
int *p = (int*)1000;
p + 1; // 结果是 1004 (跳过 4 字节)
double *d = (double*)1000;
d + 1; // 结果是 1008 (跳过 8 字节)
-
二维数组内存布局
- 二维数组(如
int a[2][3])在内存中是**连续"拉直"**存储的(行主序)。 a[0][2]的下一个元素紧挨着a[1][0]。
- 二维数组(如
⚠️ 高频易错点:指针声明
- 指针数组 (Array of Pointers):
int *p[10];[]优先级高,先结合。本质是数组,里面存了 10 个指针。
- 数组指针 (Pointer to Array):
int (*p)[10];()强制结合。本质是指针,指向一个长度为 10 的数组。- 场景:用于指向二维数组的一行。
2. 字符串
核心概念
- 没有 String 类型 :C 语言用字符数组
char[]模拟字符串。 - 结束标记 :必须以
\0结尾。"Hi"实际占用 3 字节 ('H','i','\0')。
⚠️ 陷阱:只读 vs 可写C
c
// 1. 字符数组 (栈内存) -> 可以修改
char s[] = "Hello";
s[0] = 'X'; // 变成 "Xello"
// 2. 字符串字面量指针 (常量区) -> 禁止修改 (崩溃)
char *p = "Hello";
p[0] = 'X'; // Segmentation Fault!
⚠️ 陷阱:输入读取
scanf("%s", s): 遇到空格就停止。输入 "Hello World" -> 只读入 "Hello"。fgets(s, size, stdin): 可以读取带空格的整行。
3. 函数
核心概念
-
值传递 (Pass by Value)
- 默认情况下,函数参数传递的是复印件。修改形参不会影响实参。
-
传址调用 (Pass by Reference)
- 想修改外面的变量,必须传地址。
c
// ✅ 标准交换函数
void swap(int *x, int *y) {
int temp = *x; // 1. 取出 x 指向的值
*x = *y; // 2. 修改 x 指向的内存
*y = temp; // 3. 修改 y 指向的内存
}
// 调用:swap(&a, &b);
C⚠️ 致命错误:悬空指针
- 绝对禁止返回局部变量的地址。
c
int* error_func() {
int a = 10;
return &a; // ❌ 函数结束 a 就被销毁了,返回的地址是废的
}
静态变量
static int x = 0;: 记忆力超强。函数结束不销毁,下次调用保留上次的值。
4. 结构体
核心概念
- 定义与大小
c
struct Student {
char name[20]; // 20字节
int age; // 4字节
};
// sizeof(struct Student) 至少 24 字节
-
成员访问
- 变量访问:
stu.age(点号) - 指针访问:
p->age(箭头),等价于(*p).age
- 变量访问:
-
字符串赋值
- ❌ 错误:
stu.name = "Tom";(数组名是常量) - ✅ 正确:
strcpy(stu.name, "Tom");(需要<string.h>)
- ❌ 错误:
5. 链表
这是 C 语言最灵活的数据结构,也是考试大题热门。
5.1 结点的定义
c
struct Node {
int data; // 数据域
struct Node* next; // 指针域:指向下一个同类结点
};
5.2 创建头结点
c
struct Node* head = (struct Node*)malloc(sizeof(struct Node)); // 申请内存
head->next = NULL; // 初始为空链表,指向 NULL
5.3 遍历/输出链表
c
void print_list(struct Node* head) {
struct Node* p = head->next; // 假设带头结点,从第一个真数据开始
// 如果是不带头结点的链表,就写 p = head;
while (p != NULL) { // 只要 p 指向的地方有房子
printf("%d -> ", p->data);
p = p->next; // 关键动作:移动指针到下一格
}
printf("NULL\n");
}
5.4 插入结点
场景:把 newNode 插到 p 结点的后面。
口诀:先连后,再连前(防止断链)。
c
// 1. 新结点的右手先拉住后面的人 (B)
newNode->next = p->next;
// 2. p 的右手松开 B,改去拉住新结点
p->next = newNode;
5.5 删除结点
场景:删除 p 后面的那个结点(假设叫 target)。
口诀:先备份,再过河拆桥,最后清理门户。
c
// 1. 备份:先找到要删的目标,记下来
struct Node* target = p->next;
// 2. 拆桥:让 p 直接跳过 target,连上 target 的下一个人
p->next = target->next;
// 3. 释放:把 target 占用的内存还给系统 (防止内存泄漏)
free(target);
6. 共用体 & 枚举
- 共用体 (
union) :- 所有成员共用同一块地盘(省空间)。
- 陷阱 :写了
u.f(float),之前的u.i(int) 就被覆盖成乱码了。
- 枚举 (
enum) :enum Color { RED, GREEN, BLUE };- 本质是整数常量:RED=0, GREEN=1, BLUE=2。提高代码可读性。
7. 文件操作
操作三部曲
- 打开 :
FILE *fp = fopen("文件名", "模式"); if (fp == NULL) return; // 必须检查打开是否成功 - 读写 :
fprintf,fscanf - 关闭 :
fclose(fp);(必做!)
打开模式
"r"(Read): 只读。文件不存在则报错 (NULL)。"w"(Write): 只写 。文件存在则清空覆盖;不存在则创建。"a"(Append): 追加。在文件末尾写,不覆盖原内容。
常用函数
c
// 写入文本到文件
fprintf(fp, "Name: %s, Age: %d\n", name, age);
// 从文件读取数据
fscanf(fp, "%s %d", buffer_name, &buffer_age);