在 signed int 和 unsigned int 之间转换时是否需要类型转换?

Is a type cast necessary while converting between signed int and unsigned int?

我尝试将有符号整数分配给无符号整数。

#include <stdio.h>

int main()
{
  int a;
  unsigned int b;
  scanf("%d", &a);
  b = a;
  printf("%d %u\n", a, b);
  return 0;
}

我希望编译这个会引起一个警告,我 将 int 值分配给 unsigned int 变量。但我没有得到任何 警告。

$ gcc -std=c99 -Wall -Wextra -pedantic foo.c
$ echo -1 | ./a.out
-1 4294967295

接下来我尝试将 unsigned int 分配给 signed int。

#include <stdio.h>

int main()
{
  int a;
  unsigned int b;
  scanf("%u", &b);
  a = b;
  printf("%d %u\n", a, b);
  return 0;
}

仍然没有警告。

$ gcc -std=c99 -Wall -Wextra -pedantic bar.c
$ echo 4294967295 | ./a.out
-1 4294967295

两个问题:

  1. 为什么在这些情况下没有生成警告,即使输入得到 在转换过程中修改了吗?
  2. 在这两种情况下都需要类型转换吗?

通过将 -Wsign-conversion 选项与 gcc 一起使用可启用此警告。

-Wsign-conversion
Warn for implicit conversions that may change the sign of an integer value, like assigning a signed integer expression to an unsigned integer variable. An explicit cast silences the warning. In C, this option is enabled also by -Wconversion.

有符号到无符号的转换由标准明确定义,它只是计算模UINT_MAX+1。所以你永远不会看到警告。

无符号到有符号的转换是实现定义的,即依赖于平台。您必须查阅 gcc 的文档以查看是否以及何时将其视为错误。

而且,不,强制转换在这里毫无用处。它在转换方面的结果总是相同的,你唯一能做到的就是关闭警告,如果有的话。事实上,在 C 语言中,很少有强制转换有用的情况,而整数到整数的转换从来不在其中。

代码 1:此转换定义明确。如果 int 超出 unsigned int 的范围,则添加 UINT_MAX + 1 使其进入范围。

既然代码正确,正常,应该不会有警告。但是,您可以尝试 gcc 开关 -Wconversion,它确实会针对某些正确的转换产生警告,尤其是有符号-无符号转换。

代码 2:如果输入大于 INT_MAX,则此转换是 实现定义的。您所使用的实现很可能将其定义为代码 1 中转换的逆运算。

通常情况下,编译器不会针对在该实现上明确定义的实现定义代码发出警告。您可以再次使用 -Wconversion.

转换不是必需的,作为一般原则,应避免转换,因为它们可以隐藏错误消息。

C89 标准的作者指出,除少数特定情况外,大多数当时的编译器对有符号和无符号整数数学处理相同,即使计算的数值结果介于 INT_MAX+ 1u 和 UINT_MAX。这是导致短无符号类型应提升为 "signed int" 而不是 "unsigned int" 的规则的因素之一。虽然标准不要求实施来定义这种情况下的行为,但大多数实施都这样做了,而且似乎没有理由相信这种趋势不会继续下去。

不幸的是,gcc 的作者已经决定,如果结果可能在范围内,则需要将 unsigned char 乘以正符号 int 的代码应该在执行乘法之前将其中一个操作数转换为 unsigned INT_MAX+1u 到 UINT_MAX。如果写

unsigned multiply(int x, unsigned char y) { return x*y; }

而不是

unsigned multiply(int x, unsigned char y) { return (unsigned)x*y; }

编译器将通常生成适用于所有结果的代码 到 UINT_MAX,但有时会生成在给定时出现故障的代码 这样的值。