Почему операция 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;
}

Я предположил 15, хотя i - 1 оценивает целое число.

Ответы

Ответ 1

Из-за целых рекламных акций в стандарте 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;

Рекомендация: Включить предупреждения компилятора. По крайней мере, преобразование для назначения должно давать предупреждение об усечении.

Ответ 3

Это работает (отображает 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) продвигается до целого числа, а целочисленное деление, используемое для% in C раундов к нулю вместо отрицательной бесконечности (округление к отрицательной бесконечности - это то, как это делается в математике, науке, и другие языки программирования). Таким образом, для C% равен нулю или имеет тот же знак, что и дивиденд (в этом случае -1% 16 == -1), тогда как в математическом модуле равен нулю или имеет тот же знак, что и делитель.