无符号变量算术运算产生有符号值?减法运算行为是否符合标准?
无符号整数算术运算的疑问解答
嘿,咱们来好好拆解你遇到的这两个问题,先结合你的测试代码和输出,一步步理清楚背后的原因:
你的测试代码
#include <stdio.h> #include <stdint.h> #include <inttypes.h> int main() { uint8_t n1 = 255; uint8_t z = 0; uint8_t n = 1; printf("n1 is %" PRIu8 "\n", n1); printf("z - n is %" PRIu8 "\n", z - n); printf("n1 < z: %s\n", n1 < z ? "yes" : "no"); printf("z - n < z: %s\n", z - n < z ? "yes" : "no"); printf("(uint8_t)(z - n) < (uint8_t)z: %s\n", (uint8_t)(z - n) < (uint8_t)z ? "yes" : "no"); return 0; }
输出结果
n1 is 255 z - n is 255 n1 < z: no z - n < z: yes (uint8_t)(z - n) < (uint8_t)z: no
问题1:对无符号变量执行算术运算却得到有符号值,这是标准行为吗?
这绝对是C标准规定的正常操作!根源在于C里的整数提升规则:
- 像
uint8_t这种比int小的整数类型,在做算术运算前,会自动被提升成int(只要int能装下该无符号类型的所有值——比如uint8_t最大是255,int至少是16位,肯定没问题)。 - 所以
z - n其实是先把z(0)和n(1)转成int,算出0 - 1 = -1(这是个有符号的int值)。 - 当你用
PRIu8格式输出时,这个int值会被隐式转成uint8_t,按照无符号整数的模运算规则,-1模256就是255,所以输出255。
问题2:期望两个无符号变量相减得到无符号结果,依赖溢出特性,但结果用于其他运算时不符合预期,是标准行为还是未定义行为?
先给你吃颗定心丸:无符号整数的溢出是完全定义良好的——结果会自动取模2^N(N是类型的位数,比如uint8_t就是2^8=256)。你觉得不符合预期,是因为整数提升导致中间运算结果是有符号的int,这完全是标准规定的,不是未定义行为:
- 看
z - n < z这个判断:z - n的结果是int类型的-1,z也会被提升成int的0,所以比较的是-1 < 0,当然是真,输出yes。 - 而
(uint8_t)(z - n) < (uint8_t)z:这里你显式把z - n的int结果转成uint8_t(得到255),z转成uint8_t(0),现在是无符号整数的比较,255肯定不小于0,所以输出no。 - 如果想让整个运算过程都保持无符号类型,你可以在运算前显式转换,比如写成
(uint8_t)(z - n),或者直接用unsigned int类型来避免提升(不过uint8_t转unsigned int也会提升,但结果是无符号的)。
总的来说,你遇到的所有现象都是C标准明确规定的行为,核心就是要搞懂整数提升对运算类型的影响~
内容的提问来源于stack exchange,提问作者Noil Noil
相关产品推荐
相关产品推荐

