C/C++笔试题实战:用编译器工具链锤炼内存安全与工程能力
2026/9/20 10:12:33 网站建设 项目流程

简介:本资源是一份面向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 >= 0iint类型时存在严重隐患。当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_tssize_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 lenstrlen返回size_t,避免隐式转换截断(如 64 位系统上int只有 32 位)int len = strlen(s)在超长字符串下丢失高位,导致i计算错误
ssize_t i有符号类型支持i >= 0安全比较,且ssize_t是 POSIX 标准类型,保证与size_t位宽一致int ilen > 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];

三者内存布局本质不同:

  • 方案Anew 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:102

ASan 报告精确指出:

  • 错误类型: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差异倍数
cycles12,450,321,8901,023,456,78912.2×
cache-misses8,765,4321,234,5677.1×
instructions24,567,890,1231,890,456,78913.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::vectorstd::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 - 1pivot_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 扩展时触发。解决方案分两步:

  1. 安装 Microsoft C++ Build Tools(非 Visual Studio 全量安装):

    • 下载 Microsoft C++ Build Tools ;
    • 安装时勾选 “CMake tools for Visual Studio” 和 “Windows 10/11 SDK”。
  2. 在 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 long2^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,每次检查ii+2(即6k-16k+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_mulmod_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=1

Miller-Rabin 在1e9量级快 15 倍,且可扩展至1e18量级,而6k±11e12时已不可行。

6.4 工程决策树:何时用哪种素性测试?

输入规模推荐算法理由VSCode 调试提示
n < 1e66k±1暴力确定性、无依赖、调试简单launch.json中设置stopAtEntry: true,单步观察i循环变量
1e6 ≤ n < 1e126k±1+ 预筛小质数预先用埃氏筛生成1000内质数,先试除c_cpp_properties.json

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询