переполнение без знака с оператором модуля в C - PullRequest
5 голосов
/ 20 января 2012

Я столкнулся с ошибкой в ​​некотором написанном мною коде C, и хотя его было относительно легко исправить, я хочу лучше понять проблему, лежащую в его основе.по сути, у меня было два целых числа без знака (фактически, uint32_t), которые, когда была применена операция модуля, дали беззнаковый эквивалент отрицательного числа, числа, которое было упаковано и, таким образом, было «большим».Вот пример программы для демонстрации:

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

int main(int argc, char* argv[]) {

  uint32_t foo = -1;
  uint32_t u   = 2048;
  uint64_t ul  = 2048;

  fprintf(stderr, "%d\n", foo);
  fprintf(stderr, "%u\n", foo);
  fprintf(stderr, "%lu\n", ((foo * 2600000000) % u));
  fprintf(stderr, "%ld\n", ((foo * 2600000000) % u));
  fprintf(stderr, "%lu\n", ((foo * 2600000000) % ul));
  fprintf(stderr, "%lu\n", foo % ul);

  return 0;

}

это дает следующий вывод на моей машине x86_64:

-1
4294967295
18446744073709551104
-512
1536
2047

1536 - это число, которое я ожидал, но (uint32_t) (-512) это число, которое я получал, которое, как вы можете себе представить, немного сбило все с толку.

так что, я думаю, мой вопрос заключается в следующем: почему в этом модуле происходит работа модуля между двумя числами без знака?случай, произвести число, которое больше делителя (то есть отрицательное число)?есть ли причина, по которой такое поведение предпочтительнее?

Ответы [ 2 ]

3 голосов
/ 20 января 2012

Я думаю, причина в том, что компилятор интерпретирует литерал 2600000000 как 64-битное число со знаком, так как он не вписывается в 32-битный тип со знаком.Если вы замените число на 2600000000U, вы должны получить ожидаемый результат.

2 голосов
/ 20 января 2012

У меня нет удобной ссылки, но я уверен, что когда вы делаете это умножение, оно переводит их в int64_t, потому что оно должно привести два мультипликатора к целочисленному типу со знаком.Попробуйте 2600000000u вместо 2600000000 ....

...