为什么模运算返回意外值

Why is a modulo operation returning an unexpected value

为什么下面的代码会打印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;
}

我假定 15,尽管 i - 1 的计算结果为整数。

这是因为 -1 % n 会 return -1 而不是 n - 1 1。由于本例中的 i 是无符号 8 位整数,因此它变为 255。

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

因为 C 标准中的 integer promotions。简而言之:任何类型 "smaller" 而不是 int 在使用前都被转换为 int 。一般来说,你无法避免这种情况。

那么接下来的事情是: i 晋升为 int。表达式计算为 int(您使用的常量也是 int)。模数为 -1。然后通过赋值将其转换为 uint8_t: 255

对于 printf,然后 i 被整数提升为 int(再次):(int)255。不过,这也没什么坏处。

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


如果要确保模数给出正结果,则必须通过强制转换 i:

来评估整个表达式 unsigned
i = ((unsigned)i - 1) % 16;

建议:启用编译器警告。至少作业的转换应该给出截断警告。

这适用于(显示 15)Microsoft C 编译器(没有 stdint.h,所以我使用了 typedef):

#include <stdio.h>
typedef unsigned char uint8_t;

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

255 的原因是因为 (i - 1) 被提升为整数,并且 C 中用于 % 的整数除法向零舍入而不是负无穷大(向负无穷大舍入是它在数学中完成的方式、科学和其他编程语言)。因此,对于 C % 为零或与被除数具有相同的符号(在本例中为 -1%16 == -1),而在数学中模为零或与除数具有相同的符号。