C++核心知识点全解析(三)

1. C++ 中的 define 和const 的区别 ?

回答重点

如果单纯从定义常量的角度,优先使用const:

  1. #define 是一个预处理指令,用于定义宏,但它只是在预处理阶段进行文本替换,并不进行类型检查,且出现问题后调试起来困难。

  2. const 是一个关键字,用于定义常量,它在编译时确定类型和值,并且具有类型安全的特点。

扩展知识

  1. 作用机制:
  • #definex10 只是在预处理阶段,将所有出现的 x替换为 10,这种替换在编译之前(预处理阶段)完成,不进行任何类型检查。因此,#define 它既可以用于定义常量,也可以用于定义宏函数.

  • constintx= 10 则是在编译阶段真真实实的定义了一个类型为 int 的常量 x,并赋值为 10。这种处理不仅能保留类型信息,还能进行常规的语法和语义检查。

  1. 类型安全:
  • #define 没有类型,一切都是文本替换,所以在操作上更容易出现错误。比如#define PI 3.14,当用在一个整数运算中时可能不会得到预期的结果。

  • const 有完整的类型信息,编译器可以对其进行类型检查和类型转换。

  1. 作用域:
  • #define 定义的宏在整个源文件中保持有效,直到被 #undef(取消宏定义)

  • const 则遵循C++的作用域规则,仅在定义的作用域内有效。

  1. 调试和编译输出:
  • #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 的区别?

回答重点

一个小技巧,从后往前读:

  1. char*: 这是一个指向 char 类型数据的指针,指针以及它指向的数据都是可变的。可以改变指针的指向和指向的数据。

  2. const char*:指向const char,这是一个指向 const char类型数据的指针。指针本身是可变的,但指针指向的数据是不可变的。简单来说,可以改变指针的指向,但不能改变它指向的数据内容。

  3. char* const:const修饰char*,这是一个指向 char 类型数据的常量指针。指向的数据是可变的,但指针本身是不可变的。也就是说,不能改变指针的指向,但能修改指向的数据。

  4. const char*const: 这是一个指向 const char 类型数据的常量指针。指针和指向的数据都是不可变的,也就是既不能改变指针的指向,也不能修改指针指向的数据。


3. C++ 中 inline 的作用?它有什么优缺点?

回答重点

inline的作用是建议 编译器将函数调用替换为函数体,以减少函数调用的开销,和宏比较类似。使用 inline 函数的目的一定是希望可以提高程序的运行效率,特别是那些频繁调用的小函数。

优点:

  1. 降低函数调用的开销,原理就是因为省去了调用和返回的指令开销。

  2. 如果函数体较小,可以提高代码执行的效率。

缺点:

  1. 容易导致代码膨胀,整个可执行程序体积变大,特别是当inline函数体较大且被多次调用时。

  2. 内联是一种建议,编译器可以选择忽略inline 关键字。


4. C++ 中 数组 和 指针的区别

回答重点

主要的区别可以总结为以下几点:

1. 内存分配:

  • 数组:编译器会在栈上为数组的所有元素分配连续的内存空间。

  • 指针:指针本身只占用一个内存单元(通常是4或8字节),它存储的是一个地址。初始化指针之后,可以通过动态内存分配(例如使用new或malloc)来分配内存。

2. 固定与动态大小:

  • 数组:数组的大小在声明时就确定了,数组的大小需要是常量,无法在运行时改变。

  • 指针:指针比较灵活,它指向的内存如果是堆内存,可以在运行时动态分配和释放内存,灵活性更好。

3. 类型安全性:

  • 数组:数组在声明时绑定了具体的类型,编译器在访问数组时可以进行类型检查。

  • 指针:指针声明时也有类型,但指针所指向的内存地址可以重新赋值,容易引起类型不匹配的问题,可能导致运行时错误,特别是指针类型经常转换的场景,比如int*转void*等等。

4. 运算操作:

  • 数组:数组名可以看作是数组首元素的常量指针,但不能直接进行算术运算(如++或--)

  • 指针:指针变量可以直接进行算术运算,比如递增、递减操作,从而访问不同的位置。


5. C++ 中 sizeof 和 strlen 的区别?

回答重点

两者的功能其实有很大区别:

  1. sizeof是一个编译时运算符,用于获取一个类型或者对象的大小(以字节为单位)。sizeof 在编译时计算结果,不涉及实际内容。

  2. strlen是一个库函数,用于计算C风格字符串的长度(不包括终止字符'0')。strlen是在运行时计算结果的,因为它需要遍历字符串内容。


6. C++ 中 extern 有什么作用?extern "C" 有什么作用?

回答重点

在C++中,"extern"关键字有两个主要作用:

  1. 声明变量的外部链接:当一个变量在一个文件中声明,但在另一个文件中定义时,我们可以使用"extern"关键字来告知编译器该变量在其他地方定义,避免编译器的编译错误。

  2. extern"C":在导出C++函数符号时,通过extern"C",可以保证导出的符号为C符号,而不是C++的符号(name mangling),这样可以更好的做兼容。比如llvm编译器导出的库,通过C符号可以做到MSVC编译器也可以正常链接使用。

扩展知识

变量的外部链接

假设我们有两个文件main.cpphelper.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

相关推荐
仰泳的熊猫1 小时前
题目1549:蓝桥杯算法提高VIP-盾神与积木游戏
数据结构·c++·算法·蓝桥杯
WW_千谷山4_sch2 小时前
MYOJ_11705:(洛谷P1137)旅行计划(经典拓扑排序)
c++·算法·动态规划·图论
Never_Satisfied2 小时前
在JavaScript / HTML中,img标签loading lazy加载时机详解
开发语言·javascript·html
FMRbpm2 小时前
string课后练习
c++·算法·新手入门
yyuan_in2 小时前
【已解决】VisualStudio写中文报错的解决办法
c++·visual studio
郝学胜-神的一滴2 小时前
高并发服务器开发:多进程与多线程实现深度解析
linux·服务器·开发语言·c++·程序人生
特种加菲猫2 小时前
C++对象模型与内存管理深度解析:从构造、友元到拷贝优化
开发语言·c++
Zhu_S W2 小时前
Java图论基础:有向图与无向图详解
开发语言·php