C++17起首选std::gcd,安全高效且自动处理边界;否则用迭代版欧几里得算法,需取abs并防除零;模板化时须确保整型、有符号及类型匹配。

用 std::gcd 最快最安全(C++17 起)
如果你的编译器支持 C++17(如 GCC 8+、Clang 7+、MSVC 2017 15.8+),直接用标准库的 std::gcd 是首选。它已针对整数类型做充分重载和边界检查,无需手写、不溢出、不崩。
使用前需包含头文件:#include ;注意它只接受非负整数,传入负数会触发未定义行为(实际常转为正数取模,但不可依赖)。
- 两个
int求最大公约数:std::gcd(a, b),自动处理a == 0或b == 0(结果为非零数的绝对值,gcd(0,0)返回 0) - 不能用于浮点数或自定义类型;若需泛型,得自己封装或用
std::common_type_t配合转型 - 性能上和手写欧几里得迭代版基本一致,但更少出错——比如不会漏掉
abs()或误写终止条件
手写欧几里得算法:递归 vs 迭代怎么选
当无法用 C++17(比如嵌入式环境或老编译器),就得自己实现。核心是反复用 a % b 替换 a,直到 b == 0。
递归写法简洁但有栈溢出风险(极端情况如 gcd(INT_MAX, 1) 会调用约 2e9 层);迭代写法无此问题,且现代 CPU 分支预测对循环很友好。
立即学习“C++免费学习笔记(深入)”;
- 推荐迭代版:
int gcd(int a, int b) { a = std::abs(a); b = std::abs(b); while (b != 0) { int r = a % b; a = b; b = r; } return a; } - 别漏掉
std::abs:C++ 中负数取模符号依赖实现(-5 % 3可能是 -2 或 1),而 GCD 定义要求非负输入 - 用
long long时,确保%操作不引发溢出——其实只要b != 0,a % b的结果绝对值一定小于|b|,所以安全
遇到 gcd(0, x) 或 gcd(x, 0) 怎么办
数学上规定 gcd(0, x) == |x|(x ≠ 0),而 gcd(0, 0) 通常定义为 0(因为所有整数都整除 0,但最大“公约”数无意义,标准库也返回 0)。
手写代码若没显式处理 0,迭代版自然满足(while (b != 0) 直接跳过循环,返回 a,即原 a 的绝对值);但递归版若没设 base case,可能无限递归或除零。
- 必须保证:输入为 0 时函数能立即返回,不要依赖
%计算(a % 0是未定义行为!) -
std::gcd已内置该逻辑,所以传0安全 - 常见错误:写成
return b == 0 ? a : gcd(b, a % b)却没先取abs(a)和abs(b),导致负数输入时a % b符号异常,最终结果错误
模板化支持任意整数类型要小心什么
想让 gcd 同时支持 int、long long、甚至 __int128?可以写模板,但要注意三件事:
- 类型必须是整型(
std::is_integral_v建议静态断言),否则%无效 - 避免隐式转换丢失精度:比如
gcd(int64_t, int32_t)应统一提升到int64_t,可用std::common_type_t推导 - 负数处理不能靠模板参数推导——
std::abs对unsigned类型编译失败,所以最好限定模板参数为有符号整型,或加static_cast转有符号再取绝对值
真正容易被忽略的是:GCD 算法本身不关心大小,但 % 运算在极小类型(如 int8_t)上会整型提升,可能掩盖溢出问题;实际项目中,除非明确需要,否则直接用 int 或 long long 更稳妥。











