使用说明:先自测「判别规则速查表」,遮住右列自答;再做「综合练习题」,最后对答案。 面试前只翻本目录,不背题。
| # | 主题 | 一句话判别规则 |
|---|---|---|
| 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 | 跨文件共享 | 声明 vs 定义 |
const volatile 只读寄存器:const 防代码写,volatile 防缓存读旧值| 写法 | 是什么 | 结合 |
|---|---|---|
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) |
| 宏 | 内联 | 普通函数 | |
|---|---|---|---|
| 类型检查 | 无 | 有 | 有 |
| 参数求值 | 可能多次 | 一次 | 一次 |
| 调用开销 | 无 | 无(若内联成功) | 有 |
| 代价 | 文本替换坑 | 代码膨胀 | 调用开销 |
union 或 char *p=(char*)&i; *p==0x78?'l':'h'htons/htonl/ntohl/ntohl 转换或放 1、与非放 0、异或翻转、移位读位。掩码统一
1U<<bit,宏必加括号,1U防符号位。
int *const p; 可以修改 *p,但不能修改 p。答:对,因为 p 是常量指针,指针指向不可改,但指向的内容可以改extern int x = 5; 是声明,不会分配内存。答:错,这个是定义,会分配内存。因为 extend 加初始化就是定义,不是声明。#define PINT int*; 后 PINT a, b; 中 a、b 都是指针。答:错,宏定义,是文本替换,不会对整行生效,所以 A 是指针,B 是 int。sizeof 在编译期计算,所以对指针参数算出的还是数组大小。答:错,退化成指针后计算的大小是指针的大小,是 4 或者 8int (*p)[5]; 中 p 是一个有 5 个指针的数组。答:错,因为括号的原因,变量名优先跟星号结合,所以 P 是一个指针,是一个指向数组的指针。struct { char c; int i; char d; }; 在 32 位平台 sizeof 是 9。答:错,是12#pragma pack(1) 会让结构体访问更快。答:错,因为使用这个之后不会对齐,会让对齐更紧,CPU访问计算变得复杂,不能一次性取4个字节, 所以访问会更慢。union { int i; char c; } u; u.i = 0x11223344; → u.c = 0x11。答:错, 是44,count++ 就绝对安全了。答:错,因为 count++ 不是原子操作,中断进来之后可能会导致值不能正确更新。static inline 函数会被编译器无条件下内联。答:错,内联函数在做函数指针后不会被内联REG &= ____;答:~(1 << 3)(REG >> 4) ____ 1;答:&char s[] = "abc"; → sizeof(s) = ____,strlen(s) = ____。答:4, 3sizeof(int*) = ____。答:4struct { char a; short b; } → sizeof = ____(32 位平台)。答:4int i = 0x12345678; char *p = (char*)&i; 若 *p == 0x78 是 ____ 端。答:小undefined reference 报错发生在 ____ 阶段。答:连接void f() { int arr[10]; return arr; }答:返回已被销毁的指针,发生悬垂const char *str = "hi"; str[0] = 'H'; 合法吗?为什么?答:不合法,这是一个被 const 修饰的字面量,不允许修改int *p = malloc(100); p = malloc(200); 有内存泄漏吗?答:内存泄露char *p; p[0] = 5; 属于哪类野指针?怎么防?答:未初始化, 将其初始化之后,再进行赋值答案解析已单独存放,避免自测时不小心看到答案:
第一批复习总结-答案.md(同目录)——完成全部 30 题后再翻开对照。
复习目标:每条规则能自己讲出"为什么",不只记住结论。