C语言递归统计二进制1的个数时出现段错误是什么原因?
问题描述
尝试编写C语言代码统计一个整数二进制表示中1的个数,代码运行时触发段错误,初步怀疑问题出在binary函数的递归逻辑,需要定位具体错误原因,同时了解段错误的常规排查方法。
原始实现代码如下:
#include <stdio.h> #include <math.h> static int num = 0; void binary(int target){ int n = 0; int a = 1; if(target != 0){ while(target >= a ){ n++; a = pow(2, n); } a = pow(2, n-1); num++; binary(target - a); } } int main() { int target = 0; scanf("%d", &target); binary(target); printf("%d",num); return 0; }
段错误根本原因
本次段错误本质是无限递归导致栈空间耗尽,触发逻辑链条如下:
- 错误使用浮点数函数
pow计算2的整数次幂:pow返回值是double类型,本身存在浮点精度误差,并不适合做整数场景的精确计算。当计算结果出现偏差时,赋值给int类型的变量a会出现截断错误。 - 递归分支没有覆盖负数场景:当
a的计算值比真实最高位权值大时,target - a会得到负数。函数进入target为负的递归分支后,while循环判断target >= a(初始a=1)永远不成立,n保持初始值0,随后计算a = pow(2, n-1) = pow(2,-1) = 0.5,截断为int后值为0。 - 无限递归触发栈溢出:此时执行
num++后,递归调用的参数是target - 0,也就是和当前参数完全相同的负数,递归永远无法触达target == 0的终止条件,函数栈帧持续压入调用栈,直到耗尽栈空间触发内存访问越界,最终抛出段错误。
除此之外,当输入值较大时,pow计算结果超过int取值范围会触发整数溢出,属于未定义行为,也可能直接导致内存访问错误。
修复方案
统计二进制1的个数不需要用浮点数计算,直接用位运算实现即可,既不会有精度问题,逻辑也更简洁,参考实现如下:
#include <stdio.h> int count_one(int target) { int cnt = 0; // 处理负数的情况:转成无符号数避免右移补符号位的问题 unsigned int n = (unsigned int)target; while (n != 0) { cnt += n & 1; // 取最低位判断是否为1 n = n >> 1; // 右移一位 } return cnt; } int main() { int target = 0; scanf("%d", &target); printf("%d", count_one(target)); return 0; }
如果追求更高效率,还可以用n = n & (n-1)的技巧,每次循环直接消掉最低位的1,循环次数等于二进制中1的个数,不需要遍历所有位。
段错误常规排查方法
- 编译阶段打开调试选项和警告:编译时加
-g -Wall -Wextra参数,编译器会直接提示类型不匹配、隐式截断等明显问题,比如本次代码中pow返回double赋值给int的问题,编译器会直接抛出警告。 - 用gdb定位崩溃位置:程序触发段错误后,在gdb中运行程序,崩溃时输入
bt命令打印调用栈,可以直接看到崩溃时的代码行、递归层级、参数值,很容易定位到无限递归、空指针访问等问题。 - 递归逻辑优先校验终止条件:写递归函数时先确认所有可能的输入分支都能走到终止条件,避免出现参数不变、无法收敛的递归分支。
- 避免跨类型滥用函数:浮点数函数天生存在精度损失,不要用来处理需要精确结果的整数逻辑。
内容的提问来源于stack exchange,提问作者Sofi
相关产品推荐
相关产品推荐

