c = a + b 和隐式转换

标签 c implicit-conversion

对于我的编译器,c 是 54464(截断 16 位)而 d 是 10176。 但是对于 gccc 是 120000 而 d 是 600000。

什么是真正的行为?行为是否未定义?或者我的编译器是错误的?

unsigned short a = 60000;
unsigned short b = 60000;
unsigned long c = a + b;
unsigned long d = a * 10;

是否有针对这些情况发出警报的选项?

Wconversion 警告:

void foo(unsigned long a);
foo(a+b);

但不警告:

unsigned long c = a + b

最佳答案

首先,您应该知道在 C 中,标准类型对于标准整数类型没有特定的精度(可表示值的数量)。它只需要每种类型的最小精度。这些导致以下典型位大小standard允许更复杂的表示:

  • char:8位
  • :16位
  • int:16 (!) 位
  • long:32位
  • long long(C99 起):64 位

注意:limits.h 中给出了实现的实际限制(这意味着一定的精度) .

其次,执行操作的类型取决于操作数的类型,而不是赋值左侧的类型(因为赋值也只是表达式)。为此,上面给出的类型按转换排名 排序。秩小于 int 的操作数首先转换为 int。对于其他操作数,将具有较小等级的操作数转换为其他操作数的类型。这些是 usual arithmetic conversions .

您的实现似乎使用了与 unsigned short 大小相同的 16 位 unsigned int,因此 ab转换为unsigned int,以16位进行运算。对于 unsigned,操作以 65536 为模(2 的 16 次方)执行 - 这称为环绕(这对于有符号类型不需要!)。然后将结果转换为 unsigned long 并分配给变量。

对于 gcc,我假设这是为 PC 或 32 位 CPU 编译的。因为这个(unsigned) int 通常有 32 位,而 (unsigned) long 至少有 32 位(必需)。因此,操作没有回绕。

注意:对于 PC,操作数被转换为 int,而不是 unsigned int。这是因为 int 已经可以表示 unsigned short 的所有值; unsigned int 不是必需的。如果操作的结果溢出 signed int,这可能会导致意外(实际上:实现定义)行为!

如果您需要定义大小的类型,请参阅 stdint.h(C99 起)的 uint16_tuint32_t。这些是 typedef 类型,具有适合您的实现的大小。

您还可以将其中一个操作数(不是整个表达式!)强制转换为结果类型:

unsigned long c = (unsigned long)a + b;

或者,使用已知大小的类型:

#include <stdint.h>
...
uint16_t a = 60000, b = 60000;
uint32_t c = (uint32_t)a + b;

请注意,根据转换规则,转换一个操作数就足够了。

更新(感谢@chux):

上面显示的转换没有问题。但是,如果 a 具有比类型转换更大的转换等级,这可能会将其值截断为较小的类型。虽然这可以很容易地避免,因为所有类型在编译时都是已知的(静态类型),但另一种方法是乘以一个想要的类型:

unsigned long c = ((unsigned long)1U * a) + b

这样,使用强制转换或 a(或 b)中给定类型的较大等级。乘法将被任何合理的编译器消除。

另一种方法,甚至可以通过 typeof() gcc 扩展来避免知道目标类型名称:

unsigned long c;

... many lines of code

c = ((typeof(c))1U * a) + b

关于c = a + b 和隐式转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31652572/

相关文章:

将 32 位有符号整数转换为 24 位有符号音频数据

c++ - 使用算术运算符时禁止隐式转换

Scala:除了空选项之外,将 None 用于其他目的

c# - 为什么 C# 允许从 Long 到 Float 的*隐式*转换,而这可能会丢失精度?

c - 为什么返回一个负的errno? (例如返回-EIO)

c - ANSI C 语法中的八进制数字 (lex)

java - scala 2.8 隐式 java 集合转换

c++ - const char* 在哪里获得指向内存地址的指针?

c - Windows中c的 sleep 功能。是否存在精度更高的函数?

c - 如何删除两个指定字符之间的字符?