C语言面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

服务端阅读 06月1日 10:35

C语言 inline 关键字有什么用?和宏的区别及使用限制详解

inline关键字向编译器建议将函数调用处展开为函数体,消除函数调用开销(压栈、跳转、返回),适合短小的频繁调用函数。inline与宏的区别在于:inline函数有类型检查,支持调试(可在调试器中单步进入),不会因括号问题产生意外行为,而宏只是文本替换,没有类型安全。但inline只是建议,编译器有权忽略——递归函数、包含循环或switch的复杂函数、虚函数通常不会被内联。函数体过长时编译器也会拒绝内联,因为代码膨胀反而降低指令缓存命中率。static inline结合了内部链接性和内联建议,适合放在头文件中,每个包含该头文件的翻译单元生成独立副本,不产生链接冲突。头文件中定义inline函数需加static或inline,否则多个翻译单元包含时会产生重复定义错误。C99和C++的inline语义不同:C99中inline函数不提供外部链接定义,需要在一个翻译单元中提供extern声明;C++中inline函数允许在多个翻译单元中定义,只要定义相同即可。追问编译器如何决定是否内联?现代编译器基于启发式算法:函数体小(通常10行以内)、无循环和递归、被频繁调用则倾向内联。编译选项如-O2/-O3会提高内联积极性,attribute((always_inline))可强制内联。inline会导致代码膨胀吗?会。每次内联展开都复制一份函数体到调用点,小函数开销可忽略,大函数内联后二进制体积显著增大,可能导致指令缓存命中率下降,反而降低性能。这是编译器保守内联的主要原因。C++中inline函数和宏定义的取舍?优先inline函数。宏没有作用域、没有类型检查、参数可能被多次求值(如#define SQUARE(x) ((x)*(x)),SQUARE(i++)会i自增两次)。inline函数在所有这些方面都更安全,性能相同。C99和C++的inline链接差异是什么?C++中inline函数可出现在多个翻译单元,链接器合并为一份,不违反ODR。C99中inline函数默认无外部链接,需要某处提供extern inline定义才能被其他翻译单元使用,否则链接时可能找不到符号。
服务端阅读 06月1日 10:35

C语言结构体和C++ class 有什么区别?内存对齐/柔性数组详解

在C语言中,结构体只能定义数据成员,不能包含成员函数,默认所有成员对外可见。C++中struct和class的唯一语法区别是默认访问权限:struct成员默认public,class成员默认private;默认继承权限也同理,struct默认public继承,class默认private继承。除此之外两者功能完全等价——C++的struct同样可以有构造函数、析构函数、虚函数、继承。内存对齐方面,编译器按成员声明顺序和各自对齐要求布局,通常按最大成员对齐,可用#pragma pack或alignas调整。位域允许在结构体中以位为单位指定成员宽度,节省空间但牺牲可移植性。柔性数组是结构体末尾的未指定长度数组,配合malloc动态分配,实现变长结构体。选择原则:纯数据聚合用struct,需要封装和行为的类型用class。追问内存对齐的规则是什么?每个成员的偏移量必须是该成员对齐要求的整数倍,结构体总大小必须是最大对齐要求的整数倍。对齐要求通常等于成员大小(int为4,double为8)。对齐是为了CPU高效访问,未对齐访问在某些架构上会触发异常。柔性数组和指针有什么区别?柔性数组的内存与结构体连续分配,一次malloc搞定;指针需要额外一次分配,且内存不连续。柔性数组少一次内存间接访问,缓存更友好,但不能直接赋值和拷贝结构体。什么时候用前向声明?结构体互相引用或减少头文件依赖时使用前向声明(struct Foo;)。前向声明后只能声明指针或引用,不能访问成员或创建对象,因为编译器不知道大小。C语言中结构体可以比较相等吗?不行。C语言不支持结构体直接用==比较,需要逐成员比较或用memcmp。但memcmp在含有填充字节时可能不可靠,因为填充内容不确定。正确做法是逐字段比较。
服务端阅读 06月1日 10:35

C语言预定义宏和条件编译怎么用?__FILE__/ifdef/## 详解

C语言标准定义了一组预定义宏,在编译时由预处理器自动展开,主要用于调试和条件编译。常用宏包括:FILE展开为当前源文件名(字符串),LINE展开为当前行号(整数),DATE和TIME分别展开为编译日期和时间,func(C99)展开为当前函数名,STDC在符合标准的编译器下展开为1。条件编译通过#if、#ifdef、#ifndef、#elif、#else、#endif控制代码是否参与编译,核心指令是#ifdef和#ifndef,常用于头文件保护和平台适配。defined运算符可在#if表达式中检测宏是否定义,不关心其值。#pragma once是主流编译器扩展,等价于头文件保护但更简洁。宏的高级操作符中,#将宏参数字符串化,##将两个宏参数粘合为一个标识符。追问FILE展开的是源文件路径还是文件名?取决于编译器实现。GCC通常展开为编译时传入的路径,可能是相对路径也可能是绝对路径。同一份代码不同编译方式下FILE的值可能不同。#pragma once和#ifndef保护哪个更好?pragma once更简洁且不会宏名冲突,但不是C标准,极少数边缘情况(符号链接同一文件)可能误判。#ifndef是标准方案,可靠但冗长。实践中#pragma once已足够,大型项目两者结合使用。defined运算符和#ifdef有什么区别?ifdef MACRO只能检测单个宏,defined可用在#if表达式中组合逻辑判断,如#if defined(A) && !defined(B),灵活性更高。条件编译能检测C语言版本吗?可以。C99起定义了STDCVERSION宏,值为长整型常量如199901L(C99)、201112L(C11)。通过#if STDCVERSION >= 201112L可按版本选择性编译。