
10道经典C语言面试题深度解析1. 项目概述本文精选10道具有代表性的C语言面试题涵盖指针操作、内存管理、函数设计等核心知识点。这些题目来自实际工程面试场景能够有效检验开发者对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()函数不检查输入缓冲区大小当输入超过9个字符含结尾的\0时会导致缓冲区溢出这种溢出可能破坏栈结构导致程序崩溃或被恶意利用解决方案fgets(buff, sizeof(buff), stdin);fgets()会限制读取的字符数确保不超过缓冲区容量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; }漏洞原理strcpy()同样不检查目标缓冲区大小通过输入超长字符串可以覆盖flag变量的内存空间即使密码错误也能通过溢出修改flag值绕过验证安全实践strncpy(passwd, argv[1], sizeof(passwd)-1); passwd[sizeof(passwd)-1] \0;2.3 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; }2.4 内存泄漏场景分析#include stdio.h void main(void) { char *ptr (char *)malloc(10); if(NULL ptr) { printf(Malloc failed\n); return; } else { // Do some processing } return; }关键点单次运行不会造成持久性内存泄漏操作系统会在进程结束时回收所有内存但在循环或长期运行的服务中会导致严重问题最佳实践free(ptr); // 在return前释放内存2.5 指针移动导致的free()错误#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()返回的原始地址修改后的ptr会导致段错误(Segmentation Fault)解决方案char *original_ptr ptr; // ...操作ptr... free(original_ptr);2.6 _exit()与exit()的区别#include stdio.h void func(void) { printf(Cleanup function called\n); return; } int main(void) { int i 0; atexit(func); for(;i0xffffff;i); _exit(0); }机制差异exit()会调用atexit()注册的函数_exit()直接终止进程不执行清理在需要资源释放时应使用exit()2.7 通用函数接口设计需求设计接受任意类型参数并返回整数的函数解决方案int func(void *ptr)通过void*指针接收任意数据类型配合结构体可传递多个参数2.8 指针运算优先级#include stdio.h int main(void) { char *ptr Linux; printf([%c]\n, *ptr); printf([%c]\n, *ptr); return 0; }运算规则后缀优先级高于解引用*ptr等价于(ptr)因此先返回*ptr再执行ptr2.9 字符串常量修改问题#include stdio.h int main(void) { char *ptr Linux; *ptr T; printf([%s]\n, ptr); return 0; }根本原因字符串常量存储在只读数据段通过指针修改会导致段错误应使用字符数组存储可修改字符串正确写法char ptr[] Linux;2.10 返回局部变量地址#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]\n, *val); return 0; }问题分析函数返回局部变量的地址局部变量在函数返回后生命周期结束访问该地址会导致未定义行为解决方案int *inc(int val, int *result) { *result val 1; return result; }3. 工程实践建议安全函数选择优先使用带长度检查的函数fgets、strncpy等避免使用不安全的传统函数gets、strcpy等内存管理原则确保每个malloc()都有对应的free()使用工具如Valgrind检测内存泄漏指针操作规范避免修改malloc()返回的原始指针对指针运算保持高度警惕函数设计准则main()必须返回int类型不返回局部变量的地址或引用字符串处理区分字符串常量和字符数组修改字符串内容应使用字符数组