C语言常见安全隐患与面试题解析
1. 10道C语言面试题深度解析作为一名在嵌入式领域摸爬滚打多年的老鸟我经常需要面试新人。今天整理10个C语言经典面试题都是实际工作中容易踩坑的点。这些题目覆盖指针、内存管理、函数调用等核心概念建议收藏反复琢磨。2. 常见函数的安全隐患2.1 gets()函数的缓冲区溢出问题先看这段代码#include stdio.h int main(void) { char buff[10]; memset(buff, 0, sizeof(buff)); gets(buff); printf(The buffer entered is [%s],buff); return 0; }问题分析 gets()函数从stdin读取输入时完全不检查目标缓冲区大小。如果用户输入超过9个字符留1字节给\0就会导致缓冲区溢出。这种漏洞可能被利用来执行任意代码。解决方案 改用fgets()函数fgets(buff, sizeof(buff), stdin);实际经验 在嵌入式系统中这种漏洞尤其危险。我曾见过一个设备因为串口接收处理用了gets()被恶意数据搞崩溃。建议所有输入处理都使用带长度检查的函数。2.2 strcpy()的安全隐患密码验证代码#include stdio.h int main(int argc, char *argv[]) { int flag 0; char passwd[10]; memset(passwd, 0, sizeof(passwd)); strcpy(passwd, argv[1]); if(0 strcmp(LinuxGeek, passwd)) { flag 1; } if(flag) { printf(Password cracked\n); } else { printf(Incorrect passwd\n); } return 0; }漏洞利用 输入超长字符串如aaaaaaaaaaaaa会覆盖flag变量即使密码错误也能通过验证。解决方案 使用strncpy()并确保目标缓冲区足够大strncpy(passwd, argv[1], sizeof(passwd)-1); passwd[sizeof(passwd)-1] \0;开发建议 现代编译器如gcc默认开启栈保护需要用-fno-stack-protector选项才能复现这种漏洞。但实际开发中千万不要禁用栈保护3. 函数定义与内存管理3.1 main函数的返回值问题代码#include stdio.h void main(void) { char *ptr (char *)malloc(10); if(NULL ptr) { printf(Malloc failed\n); return; } else { free(ptr); } return; }规范要求 main()应返回int类型用于表示程序退出状态。0表示成功非0表示错误。这在脚本调用时特别重要。正确写法int main(void) { // ... return 0; }3.2 内存泄漏问题问题代码#include stdio.h void main(void) { char *ptr (char *)malloc(10); if(NULL ptr) { printf(Malloc failed\n); return; } return; }关键点单次运行不会导致内存泄漏进程退出时OS会回收所有内存如果在循环中这样写就会造成严重泄漏嵌入式系统长时间运行必须手动释放内存检测工具 推荐使用Valgrind检测内存问题valgrind --leak-checkfull ./your_program4. 指针操作的陷阱4.1 指针移动导致free出错问题现象 输入freeze崩溃但zebra正常。#include stdio.h int main(int argc, char *argv[]) { char *ptr (char *)malloc(10); if(NULL ptr) { printf(Malloc failed\n); return -1; } else if(argc 1) { printf(Usage\n); } else { memset(ptr, 0, 10); strncpy(ptr, argv[1], 9); while(*ptr ! z) { if(*ptr ) break; else ptr; } if(*ptr z) { printf(String contains z\n); } free(ptr); } return 0; }原因分析 ptr在while循环中被修改free()时已经不是malloc()返回的原始地址。解决方案 保留原始指针用于释放char *orig_ptr ptr; // ...操作ptr... free(orig_ptr);4.2 指针运算优先级代码#include stdio.h int main(void) { char *ptr Linux; printf([%c]\n,*ptr); printf([%c]\n,*ptr); return 0; }输出结果[L] [i]解释ptr等价于(ptr)先取ptr指向的值再将ptr加1。注意与(*ptr)的区别。5. 其他常见问题5.1 修改字符串常量问题代码#include stdio.h int main(void) { char *ptr Linux; *ptr T; printf([%s]\n, ptr); return 0; }问题 字符串常量存储在只读段尝试修改会导致段错误。正确做法 如需修改应使用字符数组char ptr[] Linux;5.2 返回局部变量地址问题代码#include stdio.h int *inc(int val) { int a val; a; return a; } int main(void) { int a 10; int *val inc(a); printf(Incremented value is equal to [%d], *val); return 0; }风险 局部变量a在函数返回后生命周期结束返回其地址可能导致未定义行为。解决方案返回传参的地址使用static变量动态分配内存6. 开发经验总结永远使用安全的字符串函数strncpy、snprintf等malloc和free要成对出现避免内存泄漏指针操作要谨慎注意生命周期和有效性main函数应返回int类型字符串常量不可修改需要修改时应使用数组在实际嵌入式开发中这些细节尤为重要。我曾调试过一个系统随机崩溃的问题最后发现就是因为某个函数返回了局部变量的地址。建议新人多使用静态分析工具养成良好的编码习惯。