Limesn
Limesn
发布于 2026-09-08 / 23 阅读
0

第一章 C与C++

一、C++ 源文件组织与编译执行

1. 基本C++源文件的代码组织形式(内容结构)
一个标准的 .cpp 文件通常按以下顺序组织:

  • 头文件包含(#include):引入所需的库或声明。
  • 宏定义与类型别名(#define, typedef/using)。
  • 命名空间引入(using namespace,谨慎使用)。
  • 全局变量/常量声明(通常尽量避免全局变量)。
  • 辅助函数/类的定义(或实现)。
  • 主函数入口(int main()),一般放在文件最后。

2. 如何编译执行?

  • 编译+链接一步到位(以 g++ 为例):
    g++ -std=c++17 source.cpp -o program  # 生成可执行文件
    ./program                              # Linux/macOS运行
    program.exe                            # Windows运行
    
  • 分步编译(了解底层):
    • 预处理(g++ -E):处理宏、头文件展开。
    • 编译(g++ -S):生成汇编代码。
    • 汇编(g++ -c):生成目标文件 .o/.obj。
    • 链接(g++):合并目标文件与库,生成可执行文件。

二、命名空间(Namespace)

1. 主要作用是什么?
解决命名冲突(名字污染),并对逻辑功能进行模块化分组(如 std::cout 与自定义的 mylib::cout 共存)。

2. 使用时有几种形式?

  • 定义命名空间:namespace MySpace { int a; }
  • 作用域限定符访问:MySpace::a
  • 引入整个命名空间:using namespace MySpace;(把内部名字暴露到当前作用域)
  • 引入单个成员:using MySpace::a;
  • 嵌套定义(C++17起支持简洁语法):namespace A::B { ... }
  • 匿名命名空间:namespace { int internal; }(文件内部私有,详见下方)

3. 同一作用域内能定义同名的命名空间吗?
可以。 这称为命名空间的扩展(Extension)。编译器会将同一作用域下同名的命名空间合并为一个。

namespace A { int x; }
namespace A { int y; }  // 合法,合并后 A 中有 x 和 y

关于“匿名命名空间”

匿名命名空间(namespace { ... })中的实体具有内部链接属性,仅在当前 .cpp 文件内可见,且不能被 extern 外部访问(这恰好是你上一个问题的答案延伸)。现代 C++ 强烈推荐用它替代文件内的 static 修饰符。


三、const 修饰内置类型的特点

  • 被 const 修饰的内置类型变量变为只读(常量),一旦初始化,值不可修改。
  • 必须在声明时初始化(除非是 extern 外部声明)。
  • 默认具有内部链接属性(在 C++ 中,文件作用域的 const 默认是 static 的,只在本文件可见),这使得它非常适合替代 #define 宏。

四、const 与指针的四大黄金法则(笔试必考)

你列举的四条,核心在于看 const 在 * 的左边还是右边。口诀:左定值,右定向。

表达式类型解读p 能否指向别处?*p(解引用)能否修改值?
const int * p指向常量整数的指针能(p可变)不能(*p只读)
int const * p同上(两者完全等价)能不能
int * const p常量指针,指向整数不能(p固定)能(*p可改)
const int * const p指向常量整数的常量指针不能不能

记忆技巧:忽略类型名 int,只看 const 相对 * 的位置。const 在 * 左边修饰的是指向的对象,在 * 右边修饰的是指针本身。


五、new/delete VS malloc/free 辨析(全维度对比)

对比维度new / delete(C++运算符)malloc / free(C库函数)
本质关键字/运算符(可重载)标准库函数(不可重载)
内存大小编译器自动计算(无需 sizeof)手动指定字节数(易算错)
返回类型返回具体类型指针(T*),类型安全返回 void*,必须强转
构造函数/析构函数自动调用(对象生命周期完整)不调用(只分配原始内存)
失败处理抛出 std::bad_alloc 异常(默认)返回 NULL(需手动检查)
内存重分配不支持(需手动 new 拷贝再 delete)支持 realloc 扩容
数组处理new[] 配合 delete[],会记录元素个数以便逐一析构仅分配连续空间,无元素个数概念

绝对红线:new 必须配 delete,malloc 必须配 free,严禁混用(未定义行为)。


六、引用(Reference)深度剖析

1. 什么是引用?
引用是一个已存在变量的别名(Alias)。操作引用就等同于操作原变量。

2. 引用的本质是什么?
从语法层面:它是一个变量的别名,不占用独立的存储空间(语言标准规定)。
从底层实现层面(编译器视角):本质是一个常指针(Type * const),即指针指向不可变,但编译器隐藏了取地址和解引用的操作,让你用起来像普通变量。

3. 对引用取地址,得到的是什么?
得到的是被引用变量本身的地址,而不是引用这个“别名”的地址。

int a = 10;
int &ref = a;
int *p = &ref;  // p 指向的是 a,p 的值为 &a

4. 引用 VS 指针 核心辨析(面试常考)

对比维度引用(Reference)指针(Pointer)
必须初始化必须在定义时初始化可以不初始化(但不推荐,易成野指针)
能否重新绑定不能(一旦绑定,终身指向该变量)能(可改变指向另一块内存)
是否可为空绝不能为空(必须绑定有效对象)可以为 nullptr
访问成员直接用 .(使用天然像变量)用 -> 或 *(需解引用)
自增/自减含义改变原变量的值改变指针本身的地址偏移
存储空间语法上不占空间(底层用指针实现)占栈内存空间(存放地址)

根据您提供的思维导图,这是一份关于C++核心基础知识的复习大纲。以下为您详细总结这四个核心模块的内容:

六、 掌握函数重载的特点及原理

  • 什么是函数重载?
    在同一个作用域内,允许存在多个同名函数,但这些函数的参数列表必须不同。
  • 形参列表不同有哪些表现?
    1. 参数个数不同。
    2. 参数类型不同。
    3. 参数顺序不同。
      注意:仅返回值类型不同,不能构成函数重载。
  • 函数重载的原理是什么?
    依赖于C++的**名称修饰(Name Mangling)**机制。编译器在编译阶段,会将函数名和参数列表(包括参数类型、个数)结合起来,生成一个唯一的内部符号名。链接器根据这个独特的符号名来区分不同的函数。

七、 掌握内联函数的使用场景

  • 什么时候适用?
    1. 函数体代码短小(通常建议在10行以内)。
    2. 函数被频繁调用(如简单的 getter/setter 方法)。
    3. 函数内部没有复杂的控制流(如循环、递归)。
      优点:在编译阶段将函数体直接展开,省去了函数调用压栈、跳转、返回的开销,提高运行效率。
  • 什么时候不适用?
    1. 函数体代码庞大(会导致代码膨胀,增加编译后二进制文件的大小,反而降低指令缓存命中率)。
    2. 包含复杂的循环、递归或 switch 语句。
    3. 虚函数(除非在编译期能确定具体调用对象,否则多态性会导致内联失效)。
      注意:inline 只是给编译器的一个建议,编译器有权忽略它。

八、 了解C++中异常处理的几个关键字干嘛用的

  • throw:用于抛出异常对象。当程序遇到无法处理的错误时,使用 throw 抛出一个异常。
  • try:用于包裹可能抛出异常的代码块。它定义了异常监测的范围。
  • catch:用于捕获并处理特定类型(或所有类型 ...)的异常。可以定义多个 catch 块来匹配不同的异常类型。
  • 不处理异常,程序怎么执行?
    如果异常被抛出,且沿着调用栈向上一直找都没有匹配的 catch 块,程序会调用 std::terminate()(进而调用 std::abort()),导致程序直接崩溃/异常终止。
  • 处理异常,程序怎么执行?
    异常抛出后,try 块中后续的代码立即停止执行。程序开始进行栈展开(Stack Unwinding),销毁局部对象,寻找匹配的 catch 块。执行完 catch 块内的代码后,程序会跳转到 try-catch 结构之后的代码继续执行。

九、 掌握内存布局

C++程序运行时的内存通常分为5个区域:

  1. 栈区(Stack):由编译器自动分配和释放。存放函数的局部变量、函数参数、返回地址等。空间较小,分配效率极高,但容易发生栈溢出。
  2. 堆区(Heap):由程序员手动分配和释放(new/delete,malloc/free)。空间大,分配效率相对较低,容易产生内存泄漏和内存碎片。
  3. 全局/静态存储区(Global/Static):存放全局变量、静态变量(static)。程序启动时分配,程序结束时释放。分为已初始化的 .data 段和未初始化的 .bss 段。
  4. 常量存储区(Constant):存放常量(如字符串字面量、全局 const 变量等),通常位于只读内存段,不允许修改。
  5. 代码区(Code/Text):存放程序编译后的二进制机器指令(可执行代码),通常是只读的,防止程序运行时被修改。

(注:部分面试资料也会把“全局/静态区”和“常量区”合并为“静态存储区”,变成四大区域,但具体细分更能体现基础扎实程度)