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

C与C++

C与C++

你这份复习清单涵盖了大厂笔试中最爱考的核心底层原理。为了方便你对照记忆,我把所有问题梳理成6大模块,逐一给出标准答案和深度辨析。


模块一: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 必须配 deletemalloc 必须配 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
访问成员直接用 .(使用天然像变量)->*(需解引用)
自增/自减含义改变原变量的值改变指针本身的地址偏移
存储空间语法上不占空间(底层用指针实现)占栈内存空间(存放地址)