(一))
五、C 新特性、关键字与编译原理一句话总览预处理指令在正式编译前完成文本替换关键字决定类型、作用域、生命周期、链接属性和编译器优化C11 之后的现代特性用类型安全、RAII、移动语义、零拷贝视图和标准库组件替代大量旧式 C 写法。知识点关系图源代码 ├─ 预处理阶段 │ ├─ 1. #define 宏 │ └─ 8. #include 与 #include │ ├─ 类型与声明 │ ├─ 2. typedef │ ├─ 3. register │ ├─ 4. const │ ├─ 5. extern │ ├─ 6. volatile │ └─ 10. static │ ├─ 运行时与编译机制 │ ├─ 7. dynamic_cast / RTTI │ ├─ 22/23 类知识头文件保护、inline 不在本批次 │ └─ 13. sizeof / strlen │ ├─ C 风格接口 │ └─ 11. sprintf / strcpy / memcpy │ ├─ 现代 C │ ├─ 9. const 替代 #define │ ├─ 12. C11 │ ├─ 14. C17/20/23 │ ├─ 16. string_view / span / optional │ └─ 17. RAII │ └─ 工程选择 └─ 15. STL 容器在数据处理与推理服务中的选择1. C/C 中宏定义的相关知识核心宏由预处理器处理发生在正式语法编译之前本质是文本替换。宏没有类型系统、不遵循普通作用域使用方便但容易产生优先级、重复求值和调试困难等问题。1.1 对象式宏与函数式宏#define BUFFER_SIZE 1024 #define PI 3.14159 #define MAX(a, b) ((a) (b) ? (a) : (b)) #define SQUARE(x) ((x) * (x))宏参数必须充分加括号否则会因运算符优先级出错#define BAD_ADD(a, b) a b int x BAD_ADD(1, 2) * 10; // 文本展开1 2 * 10 21而不是 30函数式宏还可能多次求值参数int n 3; int y SQUARE(n); // 展开为 ((n) * (n))n 被自增两次结果和求值顺序都危险普通内联函数或模板不会这样template class T inline T square(T x) { return x * x; }1.2 字符串化与标记拼接#把宏参数转成字符串##拼接标记#define TO_STRING(x) #x #define CONCAT(a, b) a##b const char* s TO_STRING(hello); // hello int CONCAT(value, 1) 10; // int value1 10;1.3 条件编译与头文件保护#ifdef DEBUG std::cout debug\n; #else std::cout release\n; #endif #ifndef MODULE_CORE_H #define MODULE_CORE_H class Core {}; #endif常见预定义宏std::cout __FILE__; // 文件名 std::cout __LINE__; // 行号 std::cout __DATE__; // 编译日期 std::cout __TIME__; // 编译时间1.4 使用建议宏适合条件编译、头文件保护、平台判断、日志中自动带文件名和行号但普通常量优先使用constexpr小函数优先使用inline或模板枚举集合优先使用enum class。现代 C 的原则是能用语言类型系统解决就不要让预处理器绕过类型系统。2. C/C 中 typedef 关键字的相关知识核心typedef用于给已有类型起别名提高可读性、屏蔽平台差异、简化复杂函数指针声明。它不是创建新类型而是为原类型增加一个同义名称。2.1 基本用法typedef unsigned long size_type; typedef int Integer; size_type len 100; Integer x 10;2.2 结构体与函数指针typedef struct Point { int x; int y; } Point;typedef int (*CompareFunc)(int, int); void sort_array(int* arr, int n, CompareFunc cmp) { for (int i 0; i n; i) { for (int j i 1; j n; j) { if (cmp(arr[i], arr[j]) 0) { std::swap(arr[i], arr[j]); } } } }2.3 typedef 与 #define 的区别typedef int* IntPtr; #define PTR_INT int* IntPtr a, b; // a、b 都是 int* PTR_INT c, d; // 展开为 int* c, d; c 是 int*d 是 int对比项typedef#define处理阶段编译阶段预处理阶段类型检查有无作用域遵循命名空间和块作用域文本生效直到#undef复杂声明更安全容易被展开规则坑害模板别名不直接支持不支持2.4 现代 C 推荐 usingusing IntPtr int*; using CompareFunc int (*)(int, int); template class T using Vec std::vectorT; Vecint nums; Vecfloat scores;using的阅读顺序是“新名字 原类型”更直观而且支持别名模板这是typedef做不到的。需要注意typedef int UserId; typedef int OrderId;并不会形成强类型二者仍可互相赋值若需要编译期防止不同 ID 混用应封装结构体。3. 介绍一下 register 关键字核心register是早期 C/C 的存储类说明符用来建议编译器把频繁访问的变量放入 CPU 寄存器。现代编译器的寄存器分配能力远强于人工提示因此该关键字基本失去实际意义。旧式写法long long sum(int n) { register int i; register long long result 0; for (i 1; i n; i) { result i; } return result; }它只是建议不是强制程序员希望变量在寄存器中 编译器根据寄存器压力、循环频率和优化级别决定 ├─ 有空闲寄存器 → 可能放寄存器 └─ 寄存器不足 → 仍放栈上传统 C 中register变量不能取地址因为寄存器没有内存地址register int x 1; // int* p x; // 传统 C 中错误它与其他存储类区别如下关键字生命周期作用自动局部变量作用域内默认栈上分配register作用域内建议寄存器分配static 局部变量程序运行期只初始化一次全局变量程序运行期静态存储区现代代码直接写普通变量并开启优化即可g -O2 main.cpp -o mainC17 起register不再作为存储类说明符使用。性能优化应依靠 profiler、算法优化、数据布局和缓存命中而不是盲目添加register。4. 介绍一下 const 关键字核心const表示只读约束用于变量、指针、引用、参数、返回值和成员函数。它让编译器帮助防止意外修改是 const correctness 的基础。4.1 const 变量和引用const int max_count 100; // max_count 200; // 错误 void print(const std::string s) { // s new; // 错误 std::cout s; }4.2 const 与指针int x 10, y 20; const int* p1 x; // *p1 30; // 错误不能通过 p1 修改值 p1 y; // 正确可以改指向 int* const p2 x; *p2 30; // 正确可以修改值 // p2 y; // 错误不能改指向 const int* const p3 x; // *p3 30; // 错误 // p3 y; // 错误记忆const在*左边限制指向值在*右边限制指针本身。4.3 const 成员函数class Account { private: int balance; public: explicit Account(int b) : balance(b) {} int get_balance() const { // balance 0; // 错误 return balance; } };const 对象只能调用 const 成员函数const Account account(100); account.get_balance(); // 正确 // account.deposit(); // 错误4.4 const 与 constexprconst int a get_runtime_value(); // 运行期初始化也可以 constexpr int b 10 * 10; // 必须编译期可计算const表达只读constexpr表达编译期常量。mutable是极少数例外只用于锁、缓存等不影响对象逻辑状态的成员。合理使用 const 可以明确接口意图、提前暴露修改错误并让 const 对象能够安全调用只读接口。5. 介绍一下 externalextern关键字核心C 中没有external关键字正确名称是extern。它用于声明变量或函数定义在其他翻译单元中由链接器在链接阶段解析符号。// global.cpp int g_count 10;// main.cpp #include iostream extern int g_count; // 声明不分配内存 int main() { std::cout g_count \n; }声明与定义的区别extern int x; // 声明 int x; // 定义 extern int y 10; // 带初始化是定义C 命名空间作用域下的const默认内部链接跨文件共享时要写// config.cpp extern const int model_size 128;// main.cpp extern const int model_size;现代 C 更推荐头文件中使用inline constexpr int model_size 128;extern “C”C 支持重载会对函数名做 name manglingC 不会。调用 C 库时要按 C 链接规则#ifdef __cplusplus extern C { #endif int c_library_function(int); #ifdef __cplusplus } #endif编译链接关系a.cpp → a.o ┐ ├─ 链接 → 可执行文件 b.cpp → b.o ┘ extern 表示符号可能在另一个目标文件中常见错误包括只声明不定义导致 undefined reference、头文件定义全局变量导致重复定义、C 调 C 库忘记extern C导致符号不匹配。6. 介绍一下 volatile 关键字核心volatile告诉编译器被修饰内存可能在当前控制流之外被改变因此每次访问都必须真实读写不能缓存到寄存器或优化掉。它主要用于硬件寄存器和信号处理不等于多线程同步。volatile int flag 0; void wait() { while (flag 0) { // 每次循环都重新从内存读取 flag } }嵌入式内存映射 IO#define UART_STATUS (*(volatile unsigned int*)0x40001000) void wait_ready() { while ((UART_STATUS 0x01) 0) { // 每次都真实读取硬件寄存器 } }指针形式volatile int* p; // 指向 volatile int int* volatile p2; // 指针本身 volatile volatile int* volatile p3; // 值和指针都 volatilevolatile 不是线程同步工具// 错误理解volatile 不能保证原子性、内存顺序或 happens-before volatile bool stop false;多线程应使用#include atomic std::atomicbool stop{false}; void worker() { while (!stop.load(std::memory_order_acquire)) { } }场景正确工具硬件寄存器volatile信号处理标志volatile sig_atomic_t多线程共享标志std::atomic复杂临界区std::mutexconst volatile可用于程序只读但硬件会改变的状态寄存器const 限制程序写入volatile 保证每次读取。总结volatile 管的是编译器内存访问优化不管线程同步。7. 介绍一下 dynamic 在什么时候使用核心C 没有普通关键字dynamic面试通常问dynamic_cast。它用于继承层次中的运行时类型转换最典型场景是把基类指针或引用安全转换为派生类类型。class Animal { public: virtual void speak() const 0; virtual ~Animal() default; }; class Dog : public Animal { public: void speak() const override {} void guard() const {} }; void process(const Animal* animal) { const Dog* dog dynamic_castconst Dog*(animal); if (dog) { dog-guard(); } }指针转换失败返回nullptr引用没有空引用失败抛std::bad_castvoid process_ref(const Animal animal) { try { const Dog dog dynamic_castconst Dog(animal); dog.guard(); } catch (const std::bad_cast) { } }dynamic_cast依赖 RTTI因此基类必须至少有一个虚函数。它与static_cast的区别是转换检查时机类型错误时static_cast编译期不检查可能未定义行为dynamic_cast运行期指针返回 nullptr引用抛异常向上转型Dog* → Animal*天然安全可隐式完成向下转型Animal* → Dog*才需要 dynamic_cast。若代码中频繁使用 dynamic_cast通常说明基类接口抽象不足应优先把通用行为设计成虚函数。它适合反序列化、事件分发、插件接口查询等少量恢复具体类型的场景。8.#include file.h与#include file.h的区别核心二者都在预处理阶段把头文件内容插入当前位置区别主要在搜索路径和搜索顺序。尖括号通常用于系统和外部库头文件双引号通常用于项目内部头文件。#include vector // 标准库 #include nlohmann/json.hpp // 外部库 #include student.h // 当前项目头文件 #include ../common/config.h预处理过程main.cpp │ #include student.h ▼ 把 student.h 内容原样插入 ▼ 形成完整翻译单元再编译尖括号主要搜索标准库路径、系统路径和-I指定的外部 include 路径。双引号先按实现定义的本地文件规则搜索当前项目目录找不到后再回退到尖括号路径。写法常见用途搜索特征file.h标准库、第三方库、系统头偏系统/外部路径file.h项目内部头文件先本地再系统路径头文件应加保护#pragma once class Student {};或#ifndef PROJECT_STUDENT_H #define PROJECT_STUDENT_H class Student {}; #endif推荐在构建系统中统一 include 根目录g main.cpp -Iinclude然后写#include mylib/student.h避免大量../../脆弱路径。C 标准库头文件通常没有.hC 头文件的 C 封装推荐cstdio、cstring、cstdint。9. const 和 #define 有什么区别核心const是语言层面的只读实体有类型、作用域和调试信息#define是预处理文本替换没有类型检查和作用域。现代 C 常量优先使用constexpr/const。#define MAX_SIZE 100 const int max_size 100; constexpr int compile_size 100;区别表维度const/constexpr#define处理阶段编译阶段预处理阶段类型检查有无作用域类、命名空间、块作用域文本全局生效直到 undef调试可进入符号表替换后原名可能消失重复求值不存在函数式宏可能多次求值现代推荐推荐常量不推荐宏的问题#define DOUBLE(x) ((x) (x)) int n 1; int y DOUBLE(n); // n 执行两次const 不存在该问题const int n 1; int y n n;宏没有命名空间和类作用域容易污染后续代码const 常量可以属于类class Config { public: static constexpr int timeout 30; };宏仍适合条件编译、头文件保护、日志中的__FILE__/__LINE__、跨编译器属性封装。普通数值、字符串和简单函数应使用constexpr、inline函数、枚举替代。10. 介绍一下 static 关键字的作用核心static同时管理生命周期、链接属性和类级共享主要有五种用法静态局部变量、静态全局变量、静态函数、静态成员变量、静态成员函数。10.1 静态局部变量int next_id() { static int id 0; // 只初始化一次 return id; }普通局部变量每次进入函数创建离开销毁static 局部变量生命周期持续到程序结束。C11 保证其初始化线程安全。10.2 内部链接// a.cpp static void helper() {} // 只在 a.cpp 可见 static int cache 0; // 只在 a.cpp 可见现代 C 更常用匿名命名空间namespace { void helper() {} }10.3 类静态成员class Student { private: inline static int total 0; public: Student() { total; } ~Student() { --total; } static int count() { return total; // 静态函数没有 this } };静态成员变量所有对象共享静态成员函数没有this不能直接访问普通成员不能是虚函数也不能加 const。场景static 作用函数内只初始化一次保持状态文件内变量/函数内部链接避免符号冲突类成员变量所有对象共享类成员函数属于类不依赖对象多线程下共享 static 状态要使用std::mutex或std::atomic。不要滥用 static 制造隐藏全局状态否则会降低可重入性和可测试性。11. sprintf、strcpy、memcpy 有什么区别核心sprintf负责格式化输出到字符串strcpy复制以\0结尾的 C 字符串memcpy按字节复制指定长度内存。三者处理对象、结束条件、重叠规则和安全性不同。sprintfchar buf[64]; sprintf(buf, age%d, name%s, 20, Tom);它按格式串拼接文本并自动补\0返回写入字符数不包括\0。但无法限制长度容易溢出应使用snprintfsnprintf(buf, sizeof(buf), %s, long_text);strcpyconst char* src hello; char dest[16]; strcpy(dest, src); // 复制到并包含 \0它遇到源字符串\0停止不接收长度参数调用者必须保证目标容量。strncpy不一定自动补\0必须手动处理。memcpyint a[5] {1, 2, 3, 4, 5}; int b[5]; memcpy(b, a, sizeof(a)); // 严格复制 20 字节memcpy不关心字符串也不会因\0停止源和目标重叠时是未定义行为重叠场景使用memmove。函数对象长度依据是否补\0重叠sprintf格式化文本格式串和参数是不建议strcpyC 字符串遇到\0是不允许memcpy任意内存显式字节数否不允许snprintf安全格式化缓冲区长度是不建议memmove任意内存显式字节数否允许含std::string、虚函数、智能指针的非平凡 C 对象不能简单 memcpy应使用拷贝构造/赋值或容器。12. C11 的新特性都有哪些核心C11 是现代 C 分水岭引入自动类型推导、移动语义、lambda、智能指针、统一初始化、并发库、范围 for、强类型枚举和空指针常量。12.1 auto 与范围 forstd::mapstd::string, std::vectorint data; for (auto [k, v] : data) { // 结构化绑定是 C17此处展示 auto } for (auto x : vector_data) { x * 2; }12.2 nullptr、enum class、统一初始化void f(int); void f(int*); f(nullptr); // 明确空指针不会误匹配 int enum class Color { Red, Green, Blue }; Color c Color::Red; std::vectorint nums{1, 2, 3}; Point p{1, 2};12.3 lambdastd::sort(nums.begin(), nums.end(), [](int a, int b) { return a b; }); int base 10; auto add [base](int x) { return base x; };12.4 移动语义与智能指针std::string s1 large text; std::string s2 std::move(s1); // 转移资源 auto p std::make_uniqueint(10); auto q std::make_sharedint(20);12.5 其他高频特性constexpr int square(int x) { return x * x; } class Base { public: virtual void f() {} virtual ~Base() default; }; class Derived : public Base { void f() override {} }; class NonCopy { public: NonCopy() default; NonCopy(const NonCopy) delete; };还包括右值引用、noexcept、decltype、尾置返回类型、变参模板、initializer_list、委托构造、继承构造、std::thread/mutex/future/async、std::array/tuple/unordered_map/function/chrono。分组记忆类型表达auto/decltype 初始化{} / initializer_list 函数lambda/constexpr/noexcept 资源移动语义/智能指针 安全nullptr/enum class/override/final 并发thread/mutex/condition_variable/future13. sizeof 与 strlen 的区别核心sizeof是编译期运算符求类型或对象占用字节数strlen是库函数在运行时统计 C 字符串第一个\0之前的字符数。char s[] hello; sizeof(s); // 6h e l l o \0 strlen(s); // 5不包含 \0内存结构下标0 1 2 3 4 5 内容h e l l o \0 strlen → 5 sizeof → 6维度sizeofstrlen本质运算符C 库函数求值时间通常编译期运行时是否依赖\0不依赖必须依赖结果字节大小有效字符数对指针得到指针大小从地址开始扫描指针陷阱const char* p hello; sizeof(p); // 64 位平台通常是 8 strlen(p); // 5数组作为函数参数会退化为指针void bad(char s[100]) { sizeof(s); // 不是 100而是 char* 大小 }没有\0时strlen会越界扫描char buf[5] {h, e, l, l, o}; strlen(buf); // 未定义行为sizeof还会受结构体对齐影响struct Node { char c; int x; }; sizeof(Node); // 通常是 8不是 5现代 C 固定数组可用std::array::size()动态元素数量用std::vector::size()。14. C17/20/23 中哪些特性在工程中高频使用核心C17 让接口更安全、类型表达更清楚C20 提供 concepts、ranges、span、协程等高性能抽象C23 补齐 expected、mdspan、print、flat_map 等工程能力。在数据管线和推理服务中它们主要用于减少拷贝、提前暴露类型错误、简化异步和多维数据处理。C17#include optional #include string_view #include variant void print(std::string_view name); // 不拥有字符串避免临时 string std::optionalint find_index(int key) { if (key 0) return std::nullopt; return key; } using Device std::variantCpuDevice, GpuDevice, NpuDevice; std::visit([](const auto dev) { dev.run(); }, device);高频特性还包括结构化绑定、if constexpr、inline变量、折叠表达式、CTAD、std::filesystem、并行算法。C20#include concepts #include ranges #include span template std::integral T T add(T a, T b) { return a b; } float sum(std::spanconst float x); namespace rv std::views; for (int x : data | rv::filter([](int x) { return x 0; }) | rv::transform([](int x) { return x * 2; })) { }高频特性包括 concepts 约束模板、ranges 惰性管道、span 连续内存视图、coroutines 协程、modules、jthread、format、latch/barrier/semaphore。C23std::expectedint, ErrorCode parse(); std::print(value{}\n, value); std::mdspanfloat, std::dextentsstd::size_t, 2 matrix(ptr, rows, cols);expected表达成功或带原因失败mdspan把连续内存解释成多维张量视图print提供类型安全输出flat_map使用连续有序存储小配置和查找密集场景缓存友好move_only_function可持有只移动回调。选型建议C17 是稳定基线C20 是现代工程主力C23 按编译器和部署环境逐步引入。15. STL 容器在数据处理和推理服务中如何选择核心热路径默认使用连续内存容器vector/span因为张量、batch 和特征数组要求缓存友好并能直接传给底层计算库配置元数据用 map/unordered_map任务调度用 queue/priority_queuelist 在高性能数据面通常应避免。数据面struct Tensor { std::vectorfloat data; std::vectorint64_t shape; std::spanfloat view() { return data; } }; void infer(std::spanconst float input);vector连续内存可直接传给 C 接口或运行时runtime.set_input(batch.data(), batch.size() * sizeof(float));控制面std::unordered_mapstd::string, std::string config; // 精确查找 std::maplong long, Metric time_series; // 有序范围查询 std::queueRequest fifo; // FIFO std::priority_queueRequest, std::vectorRequest, Compare scheduler;场景推荐张量、特征数组vector不拥有的数据窗口span固定大小数组array精确 key 查询unordered_map有序/范围查询mapFIFOqueue优先级调度priority_queue双端增删deque高频无锁管线自定义环形缓冲/MPSC 队列list虽然已知位置插入删除 O(1)但节点独立分配、缓存差。批量过滤可用 vector 标记后压缩size_t w 0; for (size_t r 0; r items.size(); r) { if (items[r].valid) items[w] std::move(items[r]); } items.resize(w);STL 容器本身不负责线程安全多线程读写必须外部加锁、分片锁或使用专用并发队列。选型不能只看理论复杂度还要看缓存命中、内存分配次数、对象大小和队列背压。16. std::string_view、std::span、std::optional 在高性能接口中的价值核心string_view和span是非拥有视图用“指针 长度”借用已有内存避免字符串和数组复制optional用类型系统表达“可能没有值”避免魔法值和空指针歧义。string_viewvoid process(std::string_view s) { auto sub s.substr(6); // O(1)只调整指针和长度 } process(hello); // 不构造 string process(std::string(world)); // 不复制原字符串它不保证\0结尾不能无脑传给const char*C 接口。spanfloat mean(std::spanconst float values) { float total 0; for (float x : values) total x; return values.empty() ? 0 : total / values.size(); } float raw[3] {1, 2, 3}; std::vectorfloat vec {4, 5}; std::arrayfloat, 2 arr {6, 7}; mean(raw); mean(vec); mean(arr);spanT可写spanconst T只读统一了数组、vector、array 和原始连续缓冲区。optionalstd::optionalstd::string get_header(const auto headers, const std::string key) { auto it headers.find(key); if (it headers.end()) return std::nullopt; return it-second; } if (auto token get_header(headers, token)) { use(*token); } else { reject(); }生命周期风险std::string_view bad() { std::string s temporary; return s; // s 销毁view 悬空 } std::spanconst float bad2() { std::vectorfloat data(1024); return data; // vector 销毁span 悬空 }关系图所有者string / vector / array / buffer ├─ string_view借用字符区间 ├─ span借用对象区间 └─ 所有者销毁 → 视图立即失效视图适合作为函数参数和短期局部变量不适合长期保存。optional表达“可能不存在”expected表达“可能失败且有错误原因”异常用于意外错误三者分工不同。17. 为什么 RAII 是 C 资源管理的核心思想核心RAIIResource Acquisition Is Initialization即资源获取即初始化。它把资源生命周期绑定到对象生命周期构造函数获取资源析构函数释放资源对象离开作用域时自动清理即使异常导致栈展开也能释放。手动管理问题void bad() { FILE* fp fopen(a.txt, r); void* buf malloc(1024); if (error) { free(buf); fclose(fp); // 每个分支都要手动释放 return; } free(buf); fclose(fp); }RAII#include fstream #include vector void good() { std::ifstream file(a.txt); std::vectorchar buffer(1024); if (error) throw std::runtime_error(failed); // 离开作用域自动析构释放 }自定义资源类class FileHandle { private: FILE* fp; public: explicit FileHandle(const char* path, const char* mode) { fp std::fopen(path, mode); if (!fp) throw std::runtime_error(open failed); } ~FileHandle() { if (fp) std::fclose(fp); } FileHandle(const FileHandle) delete; FileHandle operator(const FileHandle) delete; FileHandle(FileHandle other) noexcept : fp(other.fp) { other.fp nullptr; } };锁是最典型 RAIIstd::mutex mtx; void push_safe(int x) { std::lock_guardstd::mutex lock(mtx); data.push_back(x); } // 自动 unlock智能指针是内存 RAIIauto p std::make_uniqueBuffer(1024); // 作用域结束自动 delete异常时构造与析构顺序构造A → B → C 异常栈展开析构C → B → A资源不仅包括内存还包括文件、socket、锁、线程、GPU context、模型会话和共享内存。现代 C 推荐 Rule of Zero业务类尽量只使用 RAII 成员不手写析构若自己管理资源则遵循 Rule of Five。RAII 把“记得释放”变成“不可能忘记释放”这是 C 资源管理的核心。第 117 项总结表题号知识点一句话结论1宏定义预处理文本替换无类型检查常量优先 constexpr2typedef类型别名现代 C 优先 using3register旧式寄存器提示现代编译器自动优化基本废弃4const只读约束构建 const correctness5extern外部链接声明extern “C” 用于 C/C 混合6volatile禁止省略硬件内存访问不等于线程同步7dynamic_cast运行时安全向下转型依赖虚函数和 RTTI8include尖括号偏系统/外部库双引号偏项目本地9const vs defineconst 有类型、作用域和调试信息10static管理生命周期、内部链接和类级共享11三类 C 函数sprintf 格式化、strcpy 复制字符串、memcpy 复制字节12C11auto、lambda、移动语义、智能指针、并发库13sizeof/strlensizeof 看字节数strlen 看\0前长度14C17/20/23optional/view/concept/ranges/span/expected/mdspan15STL 选择数据面 vector/span控制面 map/unordered_map16高性能视图view/span 借用内存optional 表达可缺失17RAII构造获取、析构释放异常安全资源管理最终记忆主线预处理#define / #include 类型声明typedef / const / extern / volatile / static 运行时识别dynamic_cast / RTTI 旧式 C 接口sprintf / strcpy / memcpy / strlen 现代 Cconstexpr、using、auto、lambda、智能指针、移动语义 高性能接口string_view / span / optional / expected 资源管理RAII / unique_ptr / lock_guard 工程选型连续内存优先 vector视图优先 span配置按有序性选 map