面试题答案
一键面试代码展开方式
- 宏定义:宏定义是在预处理阶段进行文本替换,预处理器会直接将宏调用处替换为宏定义的文本内容,不进行语法检查。例如定义宏
#define ADD(a, b) a + b
,调用ADD(3, 5)
时,预处理器直接将其替换为3 + 5
。 - 内联函数:内联函数是在编译阶段展开,编译器会将函数调用处的代码替换为函数体的代码。但编译器会对其进行语法和语义检查,以确保代码的正确性。例如定义内联函数
inline int add(int a, int b) { return a + b; }
,调用add(3, 5)
时,编译器会在编译时将其替换为函数体的具体代码。
参数处理
- 宏定义:宏参数不进行类型检查,只是简单的文本替换。这可能会导致一些意外错误,比如
#define SQUARE(x) x * x
,调用SQUARE(3 + 5)
实际展开为3 + 5 * 3 + 5
,结果并非预期的(3 + 5) * (3 + 5)
。 - 内联函数:内联函数参数具有类型,编译器会进行严格的类型检查,确保参数类型正确。如
inline int square(int x) { return x * x; }
,调用square(3 + 5)
时,编译器会按正确的运算顺序处理。
调试便利性
- 宏定义:由于宏是在预处理阶段替换,调试时看到的是展开后的代码,难以直接定位到宏定义的原始位置,增加了调试难度。如果宏定义复杂,调试过程会更加困难。
- 内联函数:调试时可以像普通函数一样设置断点,调试工具能够显示函数调用关系和参数值等信息,便于定位问题,调试相对方便。
代码体积和执行效率
- 宏定义:每次宏调用都会展开代码,如果宏定义内容较多且在多处调用,会导致代码体积大幅增加。但由于没有函数调用的开销(如栈操作等),对于简单的操作执行效率可能较高。
- 内联函数:虽然同样会展开代码增加体积,但编译器可以对其进行优化,如常量折叠等。并且内联函数遵循函数的作用域和生命周期规则,对于复杂逻辑的处理更合理。同时,内联函数的使用也受编译器优化策略的影响,如果函数体过大,编译器可能不会将其真正内联。