c语言的面试题
1、指针的概念与实际运用指针是存储地址的变量通过地址间接访问数据是 C 语言实现高效内存操作、硬件访问与灵活编程的核心工具。实际运用1.函数传参实现实参修改传址调用2.访问硬件寄存器嵌入式核心场景直接操作内存地址3.遍历数组用指针移动替代下标访问效率更高4.动态内存分配配合 malloc/free 管理堆内存5.函数指针实现回调函数、中断处理、多态机制2、static关键字的概念与用法static 为储存类型说明符修饰局部变量时将生命周期从“函数结束即销毁”延长至“整个程序运行期”值在多次调用间保持修饰全局变量或函数时将作用域从“全局可见”限制为“本文件可见”实现模块封装、防止命名冲突。3、const关键字的概念与用法const 在编译期施加只读约束防止被修饰内容被修改不改变存储位置和生命周期通过区分指针常量与常量指针实现精细控制修饰函数参数可保护输入数据相比#define 更安全且支持类型检查是现代 C 语言实现只读语义的核心机制。4、volatile关键字的概念与用法volatile用于告诉编译器“这个变量随时可能被外部硬件、中断、其他线程修改不要自作聪明优化它”。核心场景是访问硬件寄存器、中断共享变量、DMA缓冲区——这些地方变量的值会“凭空”变化不加volatile编译器可能用缓存值导致读不到最新数据程序就跑飞了。场景为什么用 volatile硬件寄存器寄存器的值会由硬件自动改变编译器不知道必须每次都重新读中断共享变量中断里改了变量主程序要能立即感知不能被编译器优化成用旧值多线程共享变量一个线程改了变量另一个线程要能立刻看到最新值DMA缓冲区DMA 会直接往内存写数据编译器不知道数据变了必须每次都重新读5、各种类型的定义语法函数指针、指针数组、数组指针等解析复杂类型遵循“从内向外、从右向左”规则先找括号确定核心[]表示数组、()表示函数、*表示指针。[]数组优先结合左边的类型*指针优先结合右边的类型。记住口诀“右数组左指针先函数后指针”就能快速判断。写法结合规则含义int *arr[5][]优先结合左边的arr→ arr 先是个数组arr 是数组元素是int*指针数组int (*p)[5]()把*p括起来*先结合 p → p 先是指针p 是指针指向int[5]数组数组指针6、String库常见函数原型以及实现方法函数原型实现思路strlensize_t strlen(const char *s)遍历字符串遇到\0停止计数器累加返回strcpychar *strcpy(char *dest, const char *src)逐个拷贝字符直到\0返回dest支持链式调用调用者需保证目标空间足够strncpychar *strncpy(char *dest, const char *src, size_t n)最多拷贝n个字符若src长度 n剩余补\0若≥ n目标不自动加\0strcatchar *strcat(char *dest, const char *src)先找到dest的\0位置再从该位置按strcpy逻辑拷贝src返回deststrcmpint strcmp(const char *s1, const char *s2)逐个比较字符返回第一个不同字符的差值s1[i] - s2[i]相等返回 0strchrchar *strchr(const char *s, int c)遍历字符串找到字符c返回位置指针未找到返回NULLc为\0时返回末尾指针7、大端与小端字节序概念核心概念大端序高位字节存低地址、小端序低位字节存低地址小端是主流 CPU 架构默认方式大端是网络协议标准判断方法通过联合或指针强制转换检查多字节数据的低地址字节内容8、原码、反码、补码的概念与计算编码正数负数特点缺陷原码符号位0 绝对值符号位1 绝对值直观减法复杂正负0反码同原码符号位不变其他取反方便减法正负0需循环进位补码同原码反码 1减法统一加法无现代计算机使用负数的补码是将负数对应的正数的原码取反后加1正数和负数的取反都是他们的相反数再减一9、结构体与联合体的区别与大小计算结构体成员独立占内存大小等于成员和加对齐填充联合体成员共用内存大小等于最大成员。结构体用于需要同时保存多个属性的场景联合体用于节省内存或同一数据不同解释的场景如协议解析、类型转换。10、什么是内存泄漏什么是内存溢出内存泄漏是指程序分配内存后未能正确释放导致已用内存只增不减、可用内存逐渐枯竭通常长期运行才暴露内存溢出是指程序申请内存时系统可用内存不足无法满足分配需求会立即抛出异常导致程序崩溃。两者关系是内存泄漏持续累积最终可能引发内存溢出。11、什么地方用const什么地方用defineconst用于需要类型检查、作用域控制和调试支持的常量如函数内、类成员、指针常量因为它更安全、更符合现代编程规范#define仅用于预处理阶段必须的场景如条件编译、头文件保护、字符串拼接因为它在预处理阶段进行文本替换缺乏类型检查和作用域控制。12、char strcpychar *destconst char *str原型为什么后面有const前面没有src 加 const表示源字符串只读不修改保证安全且允许传入字符串常量dest不加 const是因为目标缓冲区需要被写入必须可修改。13、如何防止头文件重复引用用#ifndef#define#endif配合宏#ifndef检查宏是否未定义若未定义则 #define定义该宏并执行头文件内容#endif结束条件块实现首次包含时生效、重复包含时跳过。14、 内存泄漏和内存溢出的区别内存泄漏占着不用也不还堆内存浪费长期运行才出问题。内存溢出要的太多或用得太急即时崩溃危险性更高。防范泄漏靠配对释放 工具检测溢出靠边界检查 安全函数。15、const 和 define 的区别define简单文本替换不占内存无类型适合纯文本替换。const有类型保护限制被修饰对象不可修改占用系统内存。16、 指针出参、入参的规范入参用const指针既节省拷贝开销又防止误修改让函数明确承诺“只读”出参必须传地址指针只有通过地址才能修改实参使用出参前必须检查指针非空避免因传入NULL导致的段错误17、 怎么申请常量区数据指针变量本身存储在栈区但它存储的地址指向常量区的字符串字面量所以可以通过栈上的指针变量访问常量区的数据。char *str hello; // hello 存储在常量区 const char *str2 world; // 推荐加 const 表明只读18、 指针常量与常量指针术语定义语法特点常量指针指向常量的指针const int *p或int const *p指针指向的值不可改指针本身可改指针常量指针本身是常量int *const p指针本身不可改指向的值可改类似的术语定义语法特点指针数组存放指针的数组int *arr[5]数组元素都是指针数组本身可改数组指针指向数组的指针int (*p)[5]指针指向一个数组可指向不同数组19、结构体内存大小两个准则准则1成员对齐每个结构体成员的偏移量成员相对于结构体起始地址的差值必须是其自身大小的整数倍。准则2结构体整体对齐结构体的总大小必须是其最大成员大小的整数倍。struct Example { char c; // 1字节偏移量01的倍数 int i; // 4字节偏移量必须是4的倍数 → 偏移量4 short s; // 2字节偏移量必须是2的倍数 → 偏移量8 }; // 内存布局 // 偏移: 0 1 2 3 4 5 6 7 8 9 // [c][ ][ ][ ][i][i][i][i][s][s]20、函数参数的地址传递什么时候用需要修改实参、返回多个结果、避免大结构体拷贝、操作数据结构时使用地址传递21、 0xab转换成二进制数进制转二进制每一位对应位数示例八进制三位一转3位二进制6(八进制) 110(二进制)十六进制四位一转4位二进制F(十六进制) 1111(二进制)22、 函数指针的用法定义int (*p)(int, int) add;回调函数作为参数传给其他函数如qsort函数表存入数组通过下标动态调用解耦在运行时决定调用哪个函数本质把函数当作变量传递实现动态调用。23、 编程题23.1 代码确定大小端方法一使用指针判断unsigned int num 0x12345678; char *p (char *)num; if (*p 0x78) { //小端字节序 } else { //大端字节序 }方法二使用联合体判断部分笔试题要求不使用指针union { int num; char c; }endian; endian.num 0x12345678; if (endian.c 0x78) { //小端 } else { //大端 }c只占用num的第一个字节通过读取该字节即可判断大小端。23.2 编写strcpy函数字符串拷贝函数的实现函数原型:char *strcpy(char *strDest, const char *strSrc);函数功能将strSrc中的字符串拷贝到strDestchar *strcpy(char *strDest, const char *strSrc); { assert((strDest ! NULL) (strSrc ! NULL)); //判断传参的正确性 char *address strDest; //保存目标字符串首地址用于函数返回 while((*strDest *strSrc) ! \0); // 拷贝字符串 return address; //返回目标串首地址目的是实现链式表达式 }