1. C++ 中的 define 和const 的区别 ?
回答重点
如果单纯从定义常量的角度,优先使用const:
-
#define是一个预处理指令,用于定义宏,但它只是在预处理阶段进行文本替换,并不进行类型检查,且出现问题后调试起来困难。 -
const是一个关键字,用于定义常量,它在编译时确定类型和值,并且具有类型安全的特点。
扩展知识
- 作用机制:
-
#definex10只是在预处理阶段,将所有出现的x替换为10,这种替换在编译之前(预处理阶段)完成,不进行任何类型检查。因此,#define它既可以用于定义常量,也可以用于定义宏函数. -
constintx= 10则是在编译阶段真真实实的定义了一个类型为int的常量x,并赋值为10。这种处理不仅能保留类型信息,还能进行常规的语法和语义检查。
- 类型安全:
-
#define没有类型,一切都是文本替换,所以在操作上更容易出现错误。比如#define PI 3.14,当用在一个整数运算中时可能不会得到预期的结果。 -
const有完整的类型信息,编译器可以对其进行类型检查和类型转换。
- 作用域:
-
#define定义的宏在整个源文件中保持有效,直到被#undef(取消宏定义) -
const则遵循C++的作用域规则,仅在定义的作用域内有效。
- 调试和编译输出:
-
#define的调试过程中,宏的值无法直接在调试器中看到,因为它只是一个文本替换。而且,如果宏定义中存在错误,比较难排查到具体错误。 -
const可以被调试器识别和追踪,调试时方便查看变量值和类型。
示例代码对比:
cpp
// using #define
#define PI 3.14159
int main() {
double area = PI * 10 * 10; // 没有类型检查,只是文本替换
}
// using const
const double pi = 3.14159;
int main() {
double area = pi * 10 * 10; // 类型安全,编译器知道pi是个double类型
}
总结,在定义常量方面,const 更具优势,特别是在类型安全和可调试性方面,因此在大部分情况下,我会推荐使用 const 而不是 #define.
另外,现代C++更倾向于使用 constexpr 关键字来定义编译时常量,它比 const 更有优势,因为它保证了表达式尽量在编译期计算,代码性能更优。
2. C++ 中 char*、 const char*、*char* const、const char* const 的区别?
回答重点
一个小技巧,从后往前读:
-
char*: 这是一个指向char类型数据的指针,指针以及它指向的数据都是可变的。可以改变指针的指向和指向的数据。 -
const char*:指向const char,这是一个指向const char类型数据的指针。指针本身是可变的,但指针指向的数据是不可变的。简单来说,可以改变指针的指向,但不能改变它指向的数据内容。 -
char* const:const修饰char*,这是一个指向char类型数据的常量指针。指向的数据是可变的,但指针本身是不可变的。也就是说,不能改变指针的指向,但能修改指向的数据。 -
const char*const: 这是一个指向const char类型数据的常量指针。指针和指向的数据都是不可变的,也就是既不能改变指针的指向,也不能修改指针指向的数据。
3. C++ 中 inline 的作用?它有什么优缺点?
回答重点
inline的作用是建议 编译器将函数调用替换为函数体,以减少函数调用的开销,和宏比较类似。使用 inline 函数的目的一定是希望可以提高程序的运行效率,特别是那些频繁调用的小函数。
优点:
-
降低函数调用的开销,原理就是因为省去了调用和返回的指令开销。
-
如果函数体较小,可以提高代码执行的效率。
缺点:
-
容易导致代码膨胀,整个可执行程序体积变大,特别是当inline函数体较大且被多次调用时。
-
内联是一种建议,编译器可以选择忽略inline 关键字。
4. C++ 中 数组 和 指针的区别
回答重点
主要的区别可以总结为以下几点:
1. 内存分配:
-
数组:编译器会在栈上为数组的所有元素分配连续的内存空间。
-
指针:指针本身只占用一个内存单元(通常是4或8字节),它存储的是一个地址。初始化指针之后,可以通过动态内存分配(例如使用new或malloc)来分配内存。
2. 固定与动态大小:
-
数组:数组的大小在声明时就确定了,数组的大小需要是常量,无法在运行时改变。
-
指针:指针比较灵活,它指向的内存如果是堆内存,可以在运行时动态分配和释放内存,灵活性更好。
3. 类型安全性:
-
数组:数组在声明时绑定了具体的类型,编译器在访问数组时可以进行类型检查。
-
指针:指针声明时也有类型,但指针所指向的内存地址可以重新赋值,容易引起类型不匹配的问题,可能导致运行时错误,特别是指针类型经常转换的场景,比如int*转void*等等。
4. 运算操作:
-
数组:数组名可以看作是数组首元素的常量指针,但不能直接进行算术运算(如++或--)
-
指针:指针变量可以直接进行算术运算,比如递增、递减操作,从而访问不同的位置。
5. C++ 中 sizeof 和 strlen 的区别?
回答重点
两者的功能其实有很大区别:
-
sizeof是一个编译时运算符,用于获取一个类型或者对象的大小(以字节为单位)。sizeof 在编译时计算结果,不涉及实际内容。
-
strlen是一个库函数,用于计算C风格字符串的长度(不包括终止字符'0')。strlen是在运行时计算结果的,因为它需要遍历字符串内容。
6. C++ 中 extern 有什么作用?extern "C" 有什么作用?
回答重点
在C++中,"extern"关键字有两个主要作用:
-
声明变量的外部链接:当一个变量在一个文件中声明,但在另一个文件中定义时,我们可以使用"extern"关键字来告知编译器该变量在其他地方定义,避免编译器的编译错误。
-
extern"C":在导出C++函数符号时,通过extern"C",可以保证导出的符号为C符号,而不是C++的符号(name mangling),这样可以更好的做兼容。比如llvm编译器导出的库,通过C符号可以做到MSVC编译器也可以正常链接使用。
扩展知识
变量的外部链接
假设我们有两个文件main.cpp 和 helper.cpp,并且我们想在main.cpp 中使用在 helper.cpp 中
定义的变量。
main.cpp
cpp
#include <iostream>
extern int count; // 告诉编译器,count变量在其他地方定义了
int main() {
std::cout << "Count: " << count << std::endl;
return 0;
}
helper.cpp
cpp
int count = 10;
当我们编译 main.cpp时,编译器会知道count变量在别的地方定义了。
Extern "C"
通过extern"C",可以将C++函数的符合导出为C符号
cpp
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void F() {}
#ifdef __cplusplus
}
#endif
