C语言中带模运算符的无符号溢出

C语言中带模运算符的无符号溢出,c,overflow,unsigned,modulus,C,Overflow,Unsigned,Modulus,我在编写的一些c代码中遇到了一个bug,虽然修复起来相对容易,但我希望能够更好地理解它背后的问题。本质上,我有两个无符号整数(事实上是uint32_t),当应用模运算时,产生了一个负数的无符号等价物,一个被包装的数字,因此是“大的”。下面是一个示例程序来演示: #include <stdio.h> #include <stdint.h> int main(int argc, char* argv[]) { uint32_t foo = -1; uint32_t

我在编写的一些c代码中遇到了一个bug,虽然修复起来相对容易,但我希望能够更好地理解它背后的问题。本质上,我有两个无符号整数(事实上是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;

}
1536是我期望的数字,但(uint32_t)(-512)是我得到的数字,正如你可能想象的那样,这让事情有点不对劲


所以,我想我的问题是:为什么两个无符号数之间的模运算会产生一个大于除数的数(即负数)?有什么理由喜欢这种行为吗?

我手头没有参考,但我很确定当你进行乘法时,它会将它们提升到
int64\u t
,因为它需要将两个被乘数强制为有符号整数类型。尝试
2600000000u
而不是
2600000000

我认为原因是编译器将
2600000000
文本解释为带符号的64位数字,因为它不适合带符号的32位整数。如果用
2600000000u
替换数字,您应该得到您期望的结果。

2600000000是一个整数(或者可能是一个64位的整数-长或长),这可能导致乘法结果变成(有符号)长。您在哪个平台上?实际上,OP通过向
printf
传递错误的格式字符串来调用UB。
-1
4294967295
18446744073709551104
-512
1536
2047