题目重现请看下面的代码片段请问程序的最终输出是多少#include stdio.h int main() { unsigned char i 7; // 注意这里是 unsigned char int j 0; for (; i 0; i - 3) { j; } printf(%d\n, j); return 0; }选项A. 2 B. 死循环 C. 173 D. 172正确答案C (173)在 C 语言的面试或考试中经常会出现一些看似简单实则暗藏杀机的代码片段。今天我们要拆解的这道题就考察了计算机底层数据表示的一个核心概念——无符号整数的下溢Underflow。很多同学第一眼看过去觉得是死循环或者算出一个小数字但正确答案却让人大跌眼镜。让我们一步步揭开它的真面目。 核心考点什么是unsigned char解题的关键在于变量i的类型定义unsigned char。取值范围0~255。特性它永远不可能为负数。溢出/下溢机制当计算结果超出这个范围时会发生“回绕”。如果超过 255例如 256会变成 0。如果小于 0例如 -1会变成 255。公式为(结果256)%256 。 逐步推导过程我们将程序的执行过程分为两个阶段正常减法阶段和下溢循环阶段。第一阶段正常的减法初始状态 i7,j0第 1 次循环判断70 (True)执行 j 变为 1更新 i7−34第 2 次循环判断40 (True)执行 j 变为 2更新 i4−31第 3 次循环判断10 (True)执行 j 变为 3更新 i1−3−2 ❌关键点来了第二阶段触发下溢Wrap Around由于i是无符号类型内存中无法存储 -2。根据补码运算规则(结果256)%256 。 此时-2 瞬间变成了一个很大的数254。程序并没有结束而是继续运行第三阶段漫长的“大数”循环现在 i254 我们需要计算从 254 开始每次减 3直到再次变成 0 需要多少次。这是一个等差数列问题。我们需要找到满足以下条件的最小正整数 k 循环次数254−3k≤0且结果必须能“正好”回到 0 才能终止循环因为条件是 i0只有 i0 时才停。实际上我们可以观察 i 的变化序列当前 i254 。每次减 3。我们看 254 除以 3 的余数254÷384……2 。这意味着减去 84 次 3 后还剩下 2 (254−3×842 )。再减一次 3就会再次发生下溢2−3−1→255让我们换个角度找规律我们要让 i变成 0。i 的变化路径是254→251→⋯→2→(下溢)→255→252→⋯→0。从 254 减到 2需要次数 (254−2)/384次。此时 j 增加了 84。从 2 下溢到 255需要次数1 次。此时 j 增加 1。( i 变为 255)从 255 减到 0因为 255 是 3 的倍数 (255/385)所以正好需要 85 次减完归零。此时 j 增加 85。 最终计算总次数 j (第一阶段次数) (第二阶段次数)j3(初始的3次)84(254到2)1(2变255)85(255到0)j3170173当 i 最终减为 0 时循环条件i 0不满足循环结束。输出173。 总结与启示警惕无符号数在写for循环倒计数时千万不要用unsigned类型做递减判断如i 0这会导致死循环或逻辑错误。数学思维这类题目本质上是模运算Modulo Arithmetic。对于unsigned char所有的运算都是在模 256 的意义下进行的。调试技巧如果在实际开发中遇到类似逻辑卡死尝试打印变量的值你很可能会看到变量突然从一个小数变成一个接近 255 的大数。希望这篇解析能帮你彻底搞懂无符号数的坑如果觉得有用欢迎点赞收藏最后这是助于理解的图
【C语言避坑指南】一道经典的 unsigned char 下溢陷阱题,你的答案对了吗?
发布时间:2026/6/30 2:13:48
题目重现请看下面的代码片段请问程序的最终输出是多少#include stdio.h int main() { unsigned char i 7; // 注意这里是 unsigned char int j 0; for (; i 0; i - 3) { j; } printf(%d\n, j); return 0; }选项A. 2 B. 死循环 C. 173 D. 172正确答案C (173)在 C 语言的面试或考试中经常会出现一些看似简单实则暗藏杀机的代码片段。今天我们要拆解的这道题就考察了计算机底层数据表示的一个核心概念——无符号整数的下溢Underflow。很多同学第一眼看过去觉得是死循环或者算出一个小数字但正确答案却让人大跌眼镜。让我们一步步揭开它的真面目。 核心考点什么是unsigned char解题的关键在于变量i的类型定义unsigned char。取值范围0~255。特性它永远不可能为负数。溢出/下溢机制当计算结果超出这个范围时会发生“回绕”。如果超过 255例如 256会变成 0。如果小于 0例如 -1会变成 255。公式为(结果256)%256 。 逐步推导过程我们将程序的执行过程分为两个阶段正常减法阶段和下溢循环阶段。第一阶段正常的减法初始状态 i7,j0第 1 次循环判断70 (True)执行 j 变为 1更新 i7−34第 2 次循环判断40 (True)执行 j 变为 2更新 i4−31第 3 次循环判断10 (True)执行 j 变为 3更新 i1−3−2 ❌关键点来了第二阶段触发下溢Wrap Around由于i是无符号类型内存中无法存储 -2。根据补码运算规则(结果256)%256 。 此时-2 瞬间变成了一个很大的数254。程序并没有结束而是继续运行第三阶段漫长的“大数”循环现在 i254 我们需要计算从 254 开始每次减 3直到再次变成 0 需要多少次。这是一个等差数列问题。我们需要找到满足以下条件的最小正整数 k 循环次数254−3k≤0且结果必须能“正好”回到 0 才能终止循环因为条件是 i0只有 i0 时才停。实际上我们可以观察 i 的变化序列当前 i254 。每次减 3。我们看 254 除以 3 的余数254÷384……2 。这意味着减去 84 次 3 后还剩下 2 (254−3×842 )。再减一次 3就会再次发生下溢2−3−1→255让我们换个角度找规律我们要让 i变成 0。i 的变化路径是254→251→⋯→2→(下溢)→255→252→⋯→0。从 254 减到 2需要次数 (254−2)/384次。此时 j 增加了 84。从 2 下溢到 255需要次数1 次。此时 j 增加 1。( i 变为 255)从 255 减到 0因为 255 是 3 的倍数 (255/385)所以正好需要 85 次减完归零。此时 j 增加 85。 最终计算总次数 j (第一阶段次数) (第二阶段次数)j3(初始的3次)84(254到2)1(2变255)85(255到0)j3170173当 i 最终减为 0 时循环条件i 0不满足循环结束。输出173。 总结与启示警惕无符号数在写for循环倒计数时千万不要用unsigned类型做递减判断如i 0这会导致死循环或逻辑错误。数学思维这类题目本质上是模运算Modulo Arithmetic。对于unsigned char所有的运算都是在模 256 的意义下进行的。调试技巧如果在实际开发中遇到类似逻辑卡死尝试打印变量的值你很可能会看到变量突然从一个小数变成一个接近 255 的大数。希望这篇解析能帮你彻底搞懂无符号数的坑如果觉得有用欢迎点赞收藏最后这是助于理解的图