文章目录
前言
最近帮学弟学妹们模拟面试时(场面堪比车祸现场),发现很多同学对C语言的经典问题存在理解偏差。今天咱们就扒一扒那些看似简单实则暗藏玄机的基础面试题,附带血泪踩坑经验分享!(文末有避坑指南)
1. 指针和数组到底是不是一回事?
灵魂拷问:int *p
和int arr[10]
能互相替换使用吗?
这个知识点可以说是面试官的必问题!!!90%的新手会在这里翻车。
- 本质区别:
- 数组名是常量指针(不能修改指向)
- 指针是变量指针(可以修改指向)
死亡代码示范:
int arr[5] = {1,2,3,4,5};
arr++; // 直接原地爆炸!编译报错
经典坑点:
当数组作为函数参数传递时,会退化成指针(这个特性不知道坑了多少人)。比如:
void func(int arr[10]) {
printf("%d", sizeof(arr)); // 输出4或8(指针大小)而不是40!
}
2. static关键字的三副面孔
你以为static只是用来定义静态变量?Too young!
2.1 局部变量中的static
void counter() {
static int count = 0; // 只初始化一次!!!
count++;
printf("%d", count);
}
// 连续调用三次输出1 2 3
2.2 全局变量中的static
static int secret = 42; // 限制作用域到本文件
// (这个特性在模块化开发时超有用)
2.3 函数中的static
static void hidden_func() {
// 只能在当前文件调用
}
面试杀手题:
“请用static实现单例模式”(这个题能筛掉80%的C语言选手)
3. 内存管理四大天王
3.1 malloc/calloc/realloc的区别
- malloc:单纯分配内存(可能包含垃圾值)
- calloc:分配并清零(适合数组)
- realloc:调整内存大小(可能搬家)
死亡操作示范:
int *p = malloc(10);
p++; // 这里移动了指针
free(p); // 段错误直接送你上天
3. 2 栈 vs 堆
栈 | 堆 | |
---|---|---|
分配方式 | 自动分配/释放 | 手动管理 |
大小限制 | 较小(MB级) | 较大(GB级) |
访问速度 | 快 | 较慢 |
典型问题 | 栈溢出 | 内存泄漏/碎片 |
必考题:“函数内部返回局部变量地址会发生什么?”(这个坑我当年可是踩过的)
4. volatile到底在防什么
你以为这个关键字没用?在嵌入式开发中这可是保命符!
volatile int sensor_value; // 告诉编译器别优化我!
经典场景:
- 多线程共享变量
- 硬件寄存器访问
- 信号处理程序中的变量
面试陷阱:
“volatile能保证原子性吗?”(答错这道题直接暴露项目经验不足)
5. 结构体对齐的玄学问题
来看个让人怀疑人生的例子:
struct A {
char c; // 1字节
int i; // 4字节
}; // sizeof可能是8而不是5!
对齐原则:
- 成员地址必须是其类型大小的整数倍
- 结构体总大小是最大成员大小的整数倍
实战技巧:
#pragma pack(1) // 强制1字节对齐(慎用!)
6. 预处理的黑魔法
6.1 宏定义的花式玩法
#define MAX(a,b) ({ \
typeof(a) _a = (a); \
typeof(b) _b = (b); \
_a > _b ? _a : _b; \
}) // 这个写法比普通宏安全多了
6.2 条件编译的妙用
#ifdef DEBUG
printf("调试信息");
#endif
必考陷阱:
#define SQUARE(x) x*x
int a = SQUARE(1+1); // 展开变成1+1*1+1=3 而不是4!
避坑指南(血泪总结)
- 遇到指针问题先画内存图(亲测有效)
- 多写demo验证猜想(不要相信直觉)
- 重点理解原理而非死记硬背
- 善用调试工具(gdb用好了能省一半头发)
- 定期review经典代码(linux内核源码是最好的老师)
最后送大家一句话:C语言就像一把瑞士军刀——用好了无所不能,用错了血流成河!建议各位在面试前至少完整实现一次内存池管理,绝对让你对指针的理解提升一个维度!