第一批复习总结.md 10 KB

第一批 C 语言八股 · 复习总结(23 主题)

使用说明:先自测「判别规则速查表」,遮住右列自答;再做「综合练习题」,最后对答案。 面试前只翻本目录,不背题。


一、判别规则速查表(遮右自测)

# 主题 一句话判别规则
1 volatile 变量被程序控制之外的上下文(中断/硬件/线程)改 → 加;否则不加
2 const const绑定左边最近的类型/*;最左则绑右边。绑谁谁只读
3 static 加在局部 → 改存储期(程序期);加在全局/函数 → 改链接性(锁文件内)
4 extern 一个文件定义 + 其他文件extern 声明;extern + 初始化 = 定义
5 define vs typedef define = 文本替换(无类型);typedef = 编译期真类型(有检查)
6 数组名 vs 指针 sizeof/&a 里是数组(整块);传参/运算/赋值退化&a[0]
7 数组指针 vs 指针数组 无括号int *p[N][] 优先 = 指针数组;括号 int (*p)[N] = 数组指针
8 函数指针 vs 指针函数 无括号int *f(int) = 返回指针的函数;括号 int (*f)(int) = 函数指针
9 野指针 未初始化 / 悬垂 / 越界 = 野;防 = 初始化 + 置 NULL + 查界
10 内存分配方式 全局/static→静态区(系统收);局部→栈(系统收);malloc→堆(程序员收)
11 堆 vs 栈 栈 LIFO 精确回收 → 快/小/无碎片/系统管;堆随机存取 → 慢/大/碎片/程序员管
12 泄漏/碎片 该还没还 = 泄漏(总量少);还了不齐 = 碎片(总量够不连续)
13 程序分段 指令→text;const→rodata;非零初值→data;零/未初始化→bss;局部→栈;malloc→堆
14 结构体对齐 成员偏移 = 自身对齐值整数倍;总大小 = 最大对齐值整数倍
15 struct vs union struct 累加(各自独立);union 共用(大小 = 最大成员,写一个覆盖其他)
16 位域 按分配单元分块(int 4 字节);坑 = 可移植差、不能取地址、赋值截断
17 大小端 高字节存低地址 = 大端;低字节存低地址 = 小端;通信先转网络序(大端)
18 位操作宏 置位\|=、清零 &=~、翻转 ^=、取值 >> & 1;掩码 1U<<bit
19 sizeof vs strlen sizeof 编译期按类型算字节(含 \0);strlen 运行期数到 \0 前(不含)
20 左值 vs 右值 &、能放赋值左边 = 左值;否则 = 右值。左值 ≠ 可赋值
21 原子操作 只读/写 volatile 够;读-改-写(++)要原子或临界区;单核关中断挡一切,多核只管本核
22 内联函数 短小频繁安全 → static inline;纯掩码 → 宏;大/递归/取地址 → 普通函数
23 .c → 可执行 #→预处理;语法类型→编译;.o→汇编;符号/重定位→链接

二、易混对比集中营(高频考点串讲)

三兄弟:volatile / const / static / extern

关键字 防/管什么 敌人是谁
volatile 每次读内存新值 编译器缓存优化
const 不许改(只读) 程序员误改
static 存储期 / 链接性 生命周期 / 可见范围
extern 跨文件共享 声明 vs 定义
  • const volatile 只读寄存器:const 防代码写,volatile 防缓存读旧值
  • static 局部变量被中断改 → static + volatile 叠用
  • extern 突破不了 static(内部符号不进全局符号表)

指针四连(一套"括号结合"思想)

写法 是什么 结合
int *p[10] 指针数组(10 个指针) [] 优先
int (*p)[10] 数组指针(指向 int[10]) 括号护*
int *f(int) 指针函数(返回 int*) 无括号
int (*f)(int) 函数指针(指向函数) 括号护*

内存布局全家桶(分段 + 三区)

段/区 存什么 谁管 嵌入式位置
.text 指令 Flash
.rodata const Flash
.data 非零初值全局/static 系统 初值 Flash、运行 RAM
.bss 零/未初始化全局/static 系统 RAM(不占 Flash)
局部变量 系统 RAM(函数期)
malloc 程序员 RAM(malloc→free)

宏 / 内联 / 普通函数

内联 普通函数
类型检查
参数求值 可能多次 一次 一次
调用开销 无(若内联成功)
代价 文本替换坑 代码膨胀 调用开销

大小端记忆

  • 小端:低字节存低地址(x86/ARM)
  • 大端:高字节存低地址(网络协议)
  • 判断:unionchar *p=(char*)&i; *p==0x78?'l':'h'
  • 通信:htons/htonl/ntohl/ntohl 转换

四句位操作口诀

或放 1、与非放 0、异或翻转、移位读位。掩码统一 1U<<bit,宏必加括号,1U 防符号位。


三、综合练习题(答完再对答案)

判断题(对/错 + 一句话理由)

  1. 一个只在 main() 里自增的变量,加上 volatile 能提高正确性。答:错,如果这个变量不会被其他函数、中断使用,就没有必要加 volatile。
  2. int *const p; 可以修改 *p,但不能修改 p。答:对,因为 p 是常量指针,指针指向不可改,但指向的内容可以改
  3. 函数内的 static 局部变量存放在栈上。答:错。static修饰的局部变量会存在静态区,生命周期为整个程序期
  4. extern int x = 5; 是声明,不会分配内存。答:错,这个是定义,会分配内存。因为 extend 加初始化就是定义,不是声明。
  5. #define PINT int*;PINT a, b; 中 a、b 都是指针。答:错,宏定义,是文本替换,不会对整行生效,所以 A 是指针,B 是 int。
  6. sizeof 在编译期计算,所以对指针参数算出的还是数组大小。答:错,退化成指针后计算的大小是指针的大小,是 4 或者 8
  7. int (*p)[5]; 中 p 是一个有 5 个指针的数组。答:错,因为括号的原因,变量名优先跟星号结合,所以 P 是一个指针,是一个指向数组的指针。
  8. free 之后立刻把指针置 NULL,是防止悬垂指针的防御手段。答:对,防止悬垂指针的手段之一就是释放之后立刻把指针置为 null
  9. 栈内存和堆内存一样,都会产生碎片。答:只有对内存会产生碎片,栈内存,因为严格后进先出,内存精确回收,所以不会产生碎片。
  10. struct { char c; int i; char d; }; 在 32 位平台 sizeof 是 9。答:错,是12
  11. union 的所有成员共用同一个起始地址。答:对,union的所有成员,同一个起始地址,大小为最大成员的大小,同一时间只有一个成员是有效的
  12. #pragma pack(1) 会让结构体访问更快。答:错,因为使用这个之后不会对齐,会让对齐更紧,CPU访问计算变得复杂,不能一次性取4个字节, 所以访问会更慢。
  13. 小端平台 union { int i; char c; } u; u.i = 0x11223344; → u.c = 0x11。答:错, 是44,
  14. 给共享变量加 volatile 之后,count++ 就绝对安全了。答:错,因为 count++ 不是原子操作,中断进来之后可能会导致值不能正确更新。
  15. static inline 函数会被编译器无条件下内联。答:错,内联函数在做函数指针后不会被内联

填空题

  1. 要清零 REG 的第 3 位:REG &= ____;答:~(1 << 3)
  2. 要读 REG 第 4 位的值:(REG >> 4) ____ 1;答:&
  3. char s[] = "abc";sizeof(s) = ____,strlen(s) = ____。答:4, 3
  4. 32 位平台 sizeof(int*) = ____。答:4
  5. 结构体对齐:struct { char a; short b; } → sizeof = ____(32 位平台)。答:4
  6. 判断平台大小端:int i = 0x12345678; char *p = (char*)&i;*p == 0x78 是 ____ 端。答:小
  7. 程序启动流程:上电 → 复位向量 → 设 ____ → 拷 ____ 到 RAM → 清 ____ → 跳 main()。答:SP 栈顶指针, .data, .bss
  8. undefined reference 报错发生在 ____ 阶段。答:连接
  9. 未初始化全局变量进 ____ 段,不占 ____。答:.bss, flash
  10. 网络字节序是 ____ 端,本机 x86 是 ____ 端。答:大,小

代码改错/判断

  1. 找出错误:void f() { int arr[10]; return arr; }答:返回已被销毁的指针,发生悬垂
  2. 判断:const char *str = "hi"; str[0] = 'H'; 合法吗?为什么?答:不合法,这是一个被 const 修饰的字面量,不允许修改
  3. 判断:int *p = malloc(100); p = malloc(200); 有内存泄漏吗?答:内存泄露
  4. 写出替换 bit4~bit7 为 newVal 的表达式。(oldVal & ~(0xf << 4)) | ((newVal & 0xf) << 4)
  5. 判断:char *p; p[0] = 5; 属于哪类野指针?怎么防?答:未初始化, 将其初始化之后,再进行赋值

四、答案(已分离)

答案解析已单独存放,避免自测时不小心看到答案:

第一批复习总结-答案.md(同目录)——完成全部 30 题后再翻开对照。


复习目标:每条规则能自己讲出"为什么",不只记住结论。