为什么模运算返回意外值

Raz*_*zer 16 c gcc modulo

为什么以下代码打印255

#include <stdint.h>
#include <stdio.h>

int main(void) {
  uint8_t i = 0;
  i = (i - 1) % 16;
  printf("i: %d\n", i);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我假设15,虽然i - 1评估为整数.

too*_*ite 15

因为C标准中的整数提升.简而言之:任何比使用前int转换为"更小"的类型int.一般来说,你无法避免这种情况.

那么接下来是什么: i被提升为int.表达式被评估为int(您使用的常量int也是如此).模数是-1.然后通过赋值将其转换为uint8_t:255

对于printf随后i的整数提升为int(再次): (int)255.但是,这没有坏处.

需要注意的是在C89,对a < 0,a % b不一定是负面的.它是实现定义的,本来可以15.但是,自C99以来,-1 % 16保证是-1因为除法必须产生代数商.


如果要确保模数给出正结果,则必须unsigned通过强制转换来评估整个表达式i:

i = ((unsigned)i - 1) % 16;
Run Code Online (Sandbox Code Playgroud)

建议:启用编译器警告.至少转换为赋值应该给出截断警告.

  • 它只是在C89/C90中定义的实现.链接的C11标准段落甚至在第二句中定义它,UB的内容在这里不相关. (4认同)
  • 然后阅读第一句话,包括脚注.你的第二个例子(特别是`a/16 == -1`)不满足它.C99的基本原理(第67页的6.5.5,764和165的7.20.6)也是一个有用的资源. (2认同)

Joh*_*pit 12

这是因为-1 % n会返回-1而不是n - 1 1.由于i在这种情况下是无符号8位int,因此它变为255.

1 有关负整数的模数如何在C/C++中工作的更多详细信息,请参阅此问题.