简介:本资源是一份面向C/C++初学者与求职者的笔试复习宝典,聚焦语言核心机制、算法基础与网络协议等高频考点,助力开发者高效备战技术面试与校招笔试。内容系统梳理了static关键字的三重作用、引用与指针的本质区别、实时系统特性、全局/局部变量内存分布、平衡二叉树定义、堆栈溢出成因、虚函数限制、冒泡排序复杂度、浮点数比较陷阱、TCP/IP分层模型、ARP协议原理、IP地址结构、循环链表编程实现,以及多道典型编程题(如1/2/5组合求和优化解法、零值迁移、链表按龄删节点等),并附详细思路分析与高效率代码实现。资源为单个PDF文件,大小7.36MB,排版清晰、要点凝练,适合作为随身查阅的速记手册与考前冲刺材料。目前已有64人学习下载,内容覆盖面广、解析深入,兼具知识归纳性与实战指导性。
1. 这不是题库搬运,而是用 C/C++ 笔试题反向锤炼工程能力的实战路径
很多人把《C_C++笔试题大全.pdf》当成临阵抱佛脚的“押题宝典”,打印出来划重点、背答案、考完就删——结果面试官一问“你写的这个指针交换函数,如果传入两个相同地址会怎样?”,当场卡壳。真相是:真正拉开差距的,从来不是题量,而是对每道题背后隐含的内存模型、编译行为、标准约束和边界条件的深度拆解能力。这份资料的价值,不在于“被考到”,而在于它是一套高度浓缩的 C/C++ 实战压力测试集:字符串操作暴露缓冲区意识,指针运算考验地址空间直觉,虚函数表解析倒逼理解 ABI 布局,static 关键字滥用揭示作用域与生命周期的认知断层。它适合两类人:刚结束校招但代码仍停留在“能跑就行”阶段的应届生,以及工作 3–5 年、想突破“调用 API 就行”瓶颈的嵌入式/基础软件工程师。本文不讲标准答案,只带你用 clang 编译器探针、gdb 内存快照、-fsanitize=address运行时检查,把 PDF 里每一道题变成可验证、可调试、可延伸的工程切片。
2. 从“字符串逆序输出C”到内存安全实践:用编译器工具链验证每行代码
2.1 为什么“字符串逆序输出”是高频陷阱题?——从char s[] = "hello"的栈布局说起
笔试题常写:
void reverse_print(char *s) { int len = strlen(s); for (int i = len - 1; i >= 0; i--) { printf("%c", s[i]); } }表面看逻辑正确,但i >= 0在i为int类型时存在严重隐患。当len == 0(空字符串),i初始化为-1,进入循环后s[-1]触发未定义行为(UB)。更隐蔽的是,strlen自身依赖\0终止符,若传入非\0结尾的字符数组(如char buf[10]; memcpy(buf, "test", 4);),strlen会越界扫描直到遇到随机\0,可能触发段错误或信息泄露。
提示:C 标准明确将
s[-1]定义为未定义行为,而非“访问前一个元素”。编译器可据此做激进优化,例如删除整个循环体。
2.2 用 Clang 静态分析捕获边界漏洞:三步实操
Clang 自带clang++ -fsanitize=undefined可在运行时捕获整数溢出、符号比较错误等 UB。针对上述reverse_print,我们构造测试用例:
# 编译并启用 UBSan(未定义行为检测) clang++ -std=c++17 -O2 -fsanitize=undefined -g reverse_test.cpp -o reverse_test # 测试空字符串(触发 i >= 0 的符号比较问题) echo "" | ./reverse_test # 输出:runtime error: signed integer overflow: 0 - 1 cannot be represented in type 'int'关键参数说明:
-fsanitize=undefined:启用未定义行为检测器,覆盖整数溢出、移位越界、无效指针比较等 20+ 类 UB;-O2:保持优化级别,UBSan 在优化后仍有效,避免“关优化才报错”的假象;-g:保留调试信息,错误报告中可显示具体行号。
2.3 动态内存视角:用 GDB 观察char s[] = "hello"的真实栈帧
在reverse_print入口处打断点,用 GDB 查看栈布局:
gdb ./reverse_test (gdb) b reverse_print (gdb) r (gdb) x/20xb $rsp # 查看栈顶 20 字节原始数据 # 输出示例(小端序): # 0x7fffffffe1a0: 0x68 0x65 0x6c 0x6c 0x6f 0x00 0x00 0x00 # 对应 "hello\0" + 填充字节 (gdb) p &s[0] # 确认 s 指向栈上地址 # $1 = 0x7fffffffe1a0此时若执行s[-1],实际访问0x7fffffffe19f—— 该地址属于前一个栈帧的返回地址区域,修改它将直接破坏函数返回流程。这解释了为何某些“看似无害”的越界读,在特定编译器版本下会引发崩溃而非静默错误。
2.4 安全重构方案:用size_t和ssize_t替代int
修正后的工业级实现需同时解决长度类型、空串处理、const 正确性:
#include <cstdio> #include <cstddef> // 使用 ssize_t(有符号 size_t)支持 -1 表示错误,且与 strlen 返回值兼容 void safe_reverse_print(const char *s) { if (!s) return; size_t len = strlen(s); // strlen 返回 size_t,天然无符号 if (len == 0) return; // 显式处理空串 // 用 ssize_t 避免无符号回绕:当 len=0 时,i 初始化为 -1,循环不执行 for (ssize_t i = static_cast<ssize_t>(len) - 1; i >= 0; --i) { putchar(s[i]); } putchar('\n'); }| 参数/类型 | 为什么必须用它 | 不用它的风险 |
|---|---|---|
const char *s | 声明函数不修改输入字符串,符合接口契约 | 调用者无法传递字符串字面量(如"abc"),因字面量存储在只读段 |
size_t len | strlen返回size_t,避免隐式转换截断(如 64 位系统上int只有 32 位) | int len = strlen(s)在超长字符串下丢失高位,导致i计算错误 |
ssize_t i | 有符号类型支持i >= 0安全比较,且ssize_t是 POSIX 标准类型,保证与size_t位宽一致 | int i在len > INT_MAX时溢出,UBSan 直接报错 |
3. “指针用法C++”深度解剖:从野指针到 RAII 的迁移路径
3.1 笔试题经典场景:动态分配二维数组的三种写法及其内存布局差异
常见题干:“用 new 分配一个 3×4 的 int 二维数组”。考生常写:
// 方案A:数组指针(推荐,连续内存) int (*arr)[4] = new int[3][4]; // 方案B:指针数组(易错,非连续) int **arr = new int*[3]; for (int i = 0; i < 3; ++i) arr[i] = new int[4]; // 方案C:单指针模拟(简洁但易越界) int *arr = new int[3 * 4];三者内存布局本质不同:
- 方案A:
new int[3][4]分配一块 48 字节连续内存(3×4×sizeof(int)),arr是指向int[4]数组的指针,arr[i][j]编译为*(arr + i*4 + j); - 方案B:先分配 3 个
int*指针(24 字节),再为每个指针分配 16 字节int[4],共 4 次独立堆分配,内存碎片化; - 方案C:单块 48 字节,但
arr[i][j]需手动计算arr[i*4 + j],无编译器维度检查。
注意:方案B 的
delete[] arr只释放指针数组本身,不释放arr[i]指向的内存,必须显式循环delete[] arr[i],否则内存泄漏。
3.2 用 AddressSanitizer 捕获野指针访问:构造可复现的崩溃场景
编写故意触发野指针的测试代码:
#include <iostream> int main() { int *p = new int(42); delete p; // p 成为悬垂指针(dangling pointer) std::cout << *p << "\n"; // 读取已释放内存 → ASan 报错 return 0; }编译并运行:
clang++ -std=c++17 -fsanitize=address -g dangling_test.cpp -o dangling_test ./dangling_test # 输出包含: # ================================================================= # ==12345==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010 # READ of size 4 at 0x602000000010 thread T0 # #0 0x4c9e9c in main dangling_test.cpp:6 # 0x602000000010 is located 0 bytes inside of 4-byte region [0x602000000010,0x602000000014) # freed by thread T0 here: # #0 0x4d0b20 in operator delete(void*) /.../asan_malloc_linux.cpp:123 # previously allocated by thread T0 here: # #0 0x4d0a20 in operator new(unsigned long) /.../asan_malloc_linux.cpp:102ASan 报告精确指出:
- 错误类型:
heap-use-after-free(堆内存释放后使用); - 访问地址:
0x602000000010,即p指向的地址; - 释放位置:
dangling_test.cpp:4; - 访问位置:
dangling_test.cpp:6。
3.3 工业级替代:用std::vector<std::vector<int>>和std::unique_ptr消除裸指针
现代 C++ 应彻底规避裸new/delete。针对二维数组需求:
#include <vector> #include <memory> // 方案1:vector of vector(最常用,语义清晰) std::vector<std::vector<int>> create_2d_vector(size_t rows, size_t cols) { return std::vector<std::vector<int>>(rows, std::vector<int>(cols)); } // 方案2:单块内存 + unique_ptr(高性能,零拷贝) auto create_flat_2d_array(size_t rows, size_t cols) { auto data = std::make_unique<int[]>(rows * cols); // 提供二维访问接口(需自定义 wrapper 或用 span) return std::pair{std::move(data), std::make_tuple(rows, cols)}; }对比裸指针方案,RAII 方案优势:
- 自动内存管理:
vector析构时自动释放所有内存,unique_ptr移动后原指针置空; - 异常安全:
vector构造过程中若某次new抛异常,已分配的内存由vector自动清理; - 边界检查:
vector::at()提供带范围检查的访问(operator[]不检查,但可开启_GLIBCXX_DEBUG宏); - 缓存友好:
vector<vector>中每个子vector内存连续,但整体非连续;flat_2d_array则完全连续。
3.4 嵌入式场景特化:用 placement new 和静态内存池规避动态分配
在资源受限的嵌入式环境(如 CVTE 嵌入式笔试题常考),禁止new/malloc。此时需预分配内存池:
#include <new> // for placement new class StaticPool { static constexpr size_t POOL_SIZE = 1024; alignas(std::max_align_t) static char pool_[POOL_SIZE]; static size_t offset_; public: static void* allocate(size_t size) { if (offset_ + size > POOL_SIZE) return nullptr; void* ptr = pool_ + offset_; offset_ += size; return ptr; } }; char StaticPool::pool_[StaticPool::POOL_SIZE]; size_t StaticPool::offset_ = 0; // 使用 placement new 在静态池中构造对象 int* create_in_pool() { void* mem = StaticPool::allocate(sizeof(int)); if (!mem) return nullptr; return new(mem) int(123); // placement new:不分配内存,只调用构造函数 }此方案确保:
- 所有对象生命周期由程序员显式控制(
obj->~T()显式析构); - 零动态内存分配开销;
- 内存布局完全可控,满足实时性要求。
4. “冒泡排序算法C++”的性能陷阱与现代 C++ 重写实践
4.1 笔试题中的冒泡排序:为什么它永远不该出现在生产代码中?
标准冒泡排序实现(O(n²) 时间复杂度,O(1) 空间):
void bubble_sort(int arr[], size_t n) { for (size_t i = 0; i < n - 1; ++i) { for (size_t j = 0; j < n - 1 - i; ++j) { if (arr[j] > arr[j + 1]) { std::swap(arr[j], arr[j + 1]); } } } }其致命缺陷在于:
- 缓存不友好:内层循环随机访问
arr[j]和arr[j+1],无法利用 CPU 预取; - 分支预测失败:
if (arr[j] > arr[j+1])的比较结果高度依赖数据分布,现代 CPU 分支预测器失效; - 指令级并行度低:每次迭代依赖前一次
swap结果,无法流水线化。
提示:在 100 万整数排序测试中,
std::sort(introsort)比优化版冒泡快 1000 倍以上,且std::sort是稳定、泛型、异常安全的。
4.2 用 perf 工具量化性能鸿沟:CPU cycle 与 cache miss 分析
在 Linux 下对比两种排序的底层性能:
# 编译(关闭优化以突出算法差异) g++ -O0 -g bubble_sort.cpp -o bubble_sort g++ -O0 -g stl_sort.cpp -o stl_sort # 运行 perf 统计 perf stat -e cycles,instructions,cache-references,cache-misses ./bubble_sort 100000 perf stat -e cycles,instructions,cache-references,cache-misses ./stl_sort 100000典型输出对比(10 万数据):
| 指标 | 冒泡排序 | std::sort | 差异倍数 |
|---|---|---|---|
cycles | 12,450,321,890 | 1,023,456,789 | 12.2× |
cache-misses | 8,765,432 | 1,234,567 | 7.1× |
instructions | 24,567,890,123 | 1,890,456,789 | 13.0× |
数据证明:std::sort的 cache miss 率更低,因其采用分治策略(快速排序 + 堆排序 + 插入排序混合),局部性更好;而冒泡排序强制全局扫描,反复抖动 cache line。
4.3 现代 C++ 重写:用<algorithm>和 Concepts 约束泛型排序
生产环境应直接使用标准库,并通过 Concepts 确保类型约束:
#include <algorithm> #include <iterator> #include <concepts> // C++20 Concepts 约束:要求类型支持 < 比较且可交换 template<std::random_access_iterator Iter> requires std::sortable<Iter> void modern_sort(Iter first, Iter last) { std::sort(first, last); // 直接委托给标准库 } // 使用示例:支持自定义类型 struct Person { std::string name; int age; }; bool operator<(const Person& a, const Person& b) { return a.age < b.age; // 按年龄排序 } // 编译期检查:若 Person 无 operator<,则报错 std::vector<Person> people = {{"Alice", 30}, {"Bob", 25}}; modern_sort(people.begin(), people.end());关键设计原则:
- 绝不重复造轮子:
std::sort经过数十年工业验证,支持std::ranges::sort、并行执行(std::execution::par); - Concepts 提升可维护性:编译错误信息从“模板实例化失败”变为“
Persondoes not satisfystd::sortable”,定位更快; - 迭代器抽象屏蔽容器细节:同一函数可作用于
std::vector、std::array、原生数组(int arr[10])。
4.4 笔试延伸:手写快速排序的边界条件处理要点
若面试官要求手写快排,核心考察点是边界鲁棒性:
void quick_sort(int arr[], size_t left, size_t right) { if (left >= right) return; // 必须检查 left >= right,而非 left > right! size_t pivot_idx = partition(arr, left, right); quick_sort(arr, left, pivot_idx - 1); // 递归左半区 quick_sort(arr, pivot_idx + 1, right); // 递归右半区 } size_t partition(int arr[], size_t left, size_t right) { int pivot = arr[right]; size_t i = left; // i 指向 <= pivot 的最后一个位置 for (size_t j = left; j < right; ++j) { if (arr[j] <= pivot) { std::swap(arr[i], arr[j]); ++i; } } std::swap(arr[i], arr[right]); // 将 pivot 放到最终位置 return i; }必须注意的三个边界:
- 递归终止条件:
if (left >= right)防止left=0, right=0时无限递归; - partition 循环范围:
j < right(不包含 pivot 位置),避免自身比较; - 递归调用参数:
pivot_idx - 1和pivot_idx + 1,确保子区间不重叠、不遗漏。
5. 从“华为硬件工程师笔试题”到跨平台构建:VSCode 配置 C/C++ 环境的精准实践
5.1 为什么 VSCode 配置 C/C++ 环境是嵌入式开发者的必修课?
华为、芯动科技等公司的硬件工程师笔试题常涉及寄存器映射、内存屏障、中断向量表等底层操作,这些代码无法在普通 Windows/macOS 上直接运行,必须通过交叉编译+QEMU 仿真。VSCode 凭借其轻量、插件生态和任务系统,成为构建此类环境的首选。配置目标不是“让代码高亮”,而是建立一套可复现、可调试、可 CI 的构建链:从源码编辑 → 交叉编译 → QEMU 仿真 → GDB 远程调试。
5.2 三步构建 ARM Cortex-M3 交叉编译环境(以 STM32F103 为例)
步骤1:安装 ARM GNU Toolchain 和 OpenOCD
# Ubuntu/WSL2 sudo apt update sudo apt install gcc-arm-none-eabi openocd # macOS (Homebrew) brew tap ArmMbed/homebrew-formulae brew install arm-none-eabi-gcc arm-none-eabi-binutils arm-none-eabi-gdb验证安装:
arm-none-eabi-gcc --version # 应输出 12.2.0 或更高 openocd --version # 应输出 0.12.0 或更高步骤2:VSCode 配置tasks.json实现一键编译
在项目根目录创建.vscode/tasks.json:
{ "version": "2.0.0", "tasks": [ { "label": "Build STM32", "type": "shell", "command": "arm-none-eabi-gcc", "args": [ "-mcpu=cortex-m3", "-mthumb", "-O2", "-Wall", "-I${workspaceFolder}/inc", "-T${workspaceFolder}/ld/stm32f103c8t6.ld", "-o", "${workspaceFolder}/build/firmware.elf", "${workspaceFolder}/src/startup.s", "${workspaceFolder}/src/main.c" ], "group": "build", "problemMatcher": ["$gcc"], "detail": "ARM Cortex-M3 cross-compile" } ] }关键参数说明:
-mcpu=cortex-m3:指定目标 CPU,影响指令集和优化;-mthumb:生成 Thumb 指令(16/32 位混合),节省 Flash 空间;-T:链接脚本路径,定义内存布局(Flash/RAM 地址、堆栈大小);${workspaceFolder}:VSCode 变量,自动替换为当前工作区路径。
步骤3:配置launch.json启动 QEMU + GDB 调试
{ "version": "0.2.0", "configurations": [ { "name": "Debug STM32 on QEMU", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/build/firmware.elf", "miDebuggerPath": "arm-none-eabi-gdb", "miDebuggerServerAddress": "localhost:1234", "setupCommands": [ { "description": "Enable pretty-printing", "text": "-enable-pretty-printing", "ignoreFailures": true } ], "preLaunchTask": "Build STM32" } ] }启动调试前,需在终端运行 QEMU 监听 GDB 连接:
# 启动 QEMU(模拟 STM32F103) qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic \ -kernel build/firmware.elf -S -s # -S: 启动后暂停;-s: 监听 localhost:1234此时按F5,VSCode 将自动:
- 执行
Build STM32任务; - 启动
arm-none-eabi-gdb并连接localhost:1234; - 加载符号,停在
main函数入口。
5.3 针对“CVTE嵌入式笔试题”的特殊配置:添加 CMSIS 头文件路径
CVTE 笔试题常涉及 ARM CMSIS 标准外设库。在c_cpp_properties.json中添加路径:
{ "configurations": [ { "name": "STM32-CVTE", "includePath": [ "${workspaceFolder}/**", "/usr/share/gcc-arm-none-eabi/arm-none-eabi/include/**", "/path/to/cmsis/Core/Include/**", // CMSIS Core "/path/to/cmsis/Device/ARM/STM32F1xx/Include/**" // STM32F1 设备头文件 ], "defines": ["STM32F103xB", "USE_STDPERIPH_DRIVER"], "compilerPath": "/usr/bin/arm-none-eabi-gcc" } ], "version": 4 }defines中的STM32F103xB触发 CMSIS 头文件中的条件编译,确保RCC->CR等寄存器定义正确加载。
5.4 排查常见错误:“Error: Microsoft Visual C++ 14.0 is required”
此错误实际与 VSCode 无关,而是 Python 包(如pyserial)在 Windows 上编译 C 扩展时触发。解决方案分两步:
安装 Microsoft C++ Build Tools(非 Visual Studio 全量安装):
- 下载 Microsoft C++ Build Tools ;
- 安装时勾选 “CMake tools for Visual Studio” 和 “Windows 10/11 SDK”。
在 VSCode 终端中激活环境变量:
# PowerShell 中运行(管理员权限) & "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x64 pip install pyserial # 此时可成功编译
注意:此错误与 C/C++ 开发本身无关,是 Python 生态的构建依赖。VSCode 的 C/C++ 插件(ms-vscode.cpptools)不依赖 MSVC,它只调用你配置的
compilerPath(如arm-none-eabi-gcc)。
6. “判断质数C++优化”背后的算法工程思维:从暴力到 Miller-Rabin 的渐进式落地
6.1 笔试题“判断质数”为何是算法能力的试金石?
表面看是数学题,实则考察:
- 时间复杂度敏感度:
O(√n)暴力法 vsO(k log³n)Miller-Rabin; - 数值边界意识:
int最大值2^31-1 ≈ 2e9,√n ≈ 44721,暴力法可行;但若扩展到long long(2^63-1),√n ≈ 3e9,暴力法超时; - 随机化算法接受度:Miller-Rabin 是概率算法,错误率
< 4^(-k),工程中k=5时错误率< 1e-3,远低于硬件故障率。
6.2 三层优化演进:从笔试代码到生产可用
第一层:基础优化(应对int范围)
bool is_prime_basic(int n) { if (n < 2) return false; if (n == 2) return true; if (n % 2 == 0) return false; // 排除偶数 for (int i = 3; i * i <= n; i += 2) { // 只试奇数,i*i 避免 sqrt 调用 if (n % i == 0) return false; } return true; }关键优化点:
i * i <= n替代i <= sqrt(n):避免浮点运算和类型转换开销;i += 2跳过所有偶数,减少 50% 迭代次数;- 单独处理
n == 2,避免i=2时的冗余模运算。
第二层:6k±1 优化(进一步减少迭代)
所有质数 > 3 必为6k±1形式(因6k, 6k±2, 6k±3, 6k±4均可被 2 或 3 整除):
bool is_prime_6k(int n) { if (n < 2) return false; if (n == 2 || n == 3) return true; if (n % 2 == 0 || n % 3 == 0) return false; for (int i = 5; i * i <= n; i += 6) { if (n % i == 0 || n % (i + 2) == 0) return false; } return true; }循环步长6,每次检查i和i+2(即6k-1和6k+1),迭代次数减少至基础版的1/3。
第三层:Miller-Rabin 概率素性测试(long long范围)
#include <cstdint> #include <random> uint64_t mod_mul(uint64_t a, uint64_t b, uint64_t mod) { // 防止 a*b 溢出,用二进制分解实现模乘 uint64_t res = 0; a %= mod; while (b) { if (b & 1) res = (res + a) % mod; a = (a << 1) % mod; b >>= 1; } return res; } uint64_t mod_pow(uint64_t base, uint64_t exp, uint64_t mod) { uint64_t res = 1; while (exp) { if (exp & 1) res = mod_mul(res, base, mod); base = mod_mul(base, base, mod); exp >>= 1; } return res; } bool miller_rabin(uint64_t n, int k = 5) { if (n < 2) return false; if (n == 2 || n == 3) return true; if (n % 2 == 0 || n % 3 == 0) return false; // 将 n-1 写成 d * 2^r uint64_t d = n - 1; int r = 0; while ((d & 1) == 0) { d >>= 1; r++; } // 随机基底 a ∈ [2, n-2] static std::random_device rd; static std::mt19937_64 gen(rd()); std::uniform_int_distribution<uint64_t> dis(2, n - 2); for (int i = 0; i < k; ++i) { uint64_t a = dis(gen); uint64_t x = mod_pow(a, d, n); if (x == 1 || x == n - 1) continue; bool composite = true; for (int j = 1; j < r; ++j) { x = mod_mul(x, x, n); if (x == n - 1) { composite = false; break; } } if (composite) return false; } return true; }mod_mul和mod_pow使用二进制分解避免uint64_t乘法溢出,这是 C++ 中处理大数模幂的核心技巧。
6.3 在 VSCode 中验证优化效果:用chrono测量毫秒级差异
#include <chrono> #include <iostream> int main() { uint64_t test_num = 1000000007ULL; // 大质数 auto start = std::chrono::high_resolution_clock::now(); bool result1 = is_prime_6k(static_cast<int>(test_num)); // 仅适用于 int auto end1 = std::chrono::high_resolution_clock::now(); auto start2 = std::chrono::high_resolution_clock::now(); bool result2 = miller_rabin(test_num, 5); auto end2 = std::chrono::high_resolution_clock::now(); auto ms1 = std::chrono::duration_cast<std::chrono::microseconds>(end1 - start).count(); auto ms2 = std::chrono::duration_cast<std::chrono::microseconds>(end2 - start2).count(); std::cout << "6k±1 method: " << ms1 << " μs, result=" << result1 << "\n"; std::cout << "Miller-Rabin: " << ms2 << " μs, result=" << result2 << "\n"; return 0; }编译运行(g++ -O2 prime_test.cpp -o prime_test),典型输出:
6k±1 method: 1250 μs, result=1 Miller-Rabin: 85 μs, result=1Miller-Rabin 在1e9量级快 15 倍,且可扩展至1e18量级,而6k±1在1e12时已不可行。
6.4 工程决策树:何时用哪种素性测试?
| 输入规模 | 推荐算法 | 理由 | VSCode 调试提示 |
|---|---|---|---|
n < 1e6 | 6k±1暴力 | 确定性、无依赖、调试简单 | 在launch.json中设置stopAtEntry: true,单步观察i循环变量 |
1e6 ≤ n < 1e12 | 6k±1+ 预筛小质数 | 预先用埃氏筛生成1000内质数,先试除 | 在c_cpp_properties.json中 |
本文还有配套的精品资源,点击获取