首先,您应该知道,在 C 语言中,标准类型对于标准整数类型没有特定的精度(可表示值的数量)。它只需要每种类型的最低精度。这些导致以下典型位大小,standard 允许更复杂的表示:
-
char:8 位
-
short:16 位
-
int: 16 (!) 位
-
long:32 位
-
long long(C99 起):64 位
注意:在limits.h 中给出了实现的实际限制(意味着一定的精度)。
其次,执行操作的类型取决于操作数的类型,而不是赋值左侧的类型(因为赋值也只是表达式)。为此,上面给出的类型按转化排名排序。秩小于int 的操作数首先转换为int。对于其他操作数,将具有较小等级的操作数转换为其他操作数的类型。这些是usual arithmetic conversions。
您的实现似乎使用了与unsigned short 大小相同的16 位unsigned int,因此a 和b 被转换为unsigned int,以16 位执行操作。对于unsigned,运算以 65536 为模(2 的 16 次方)执行 - 这称为回绕(签名类型不需要!)。然后将结果转换为unsigned long 并分配给变量。
对于 gcc,我假设这是为 PC 或 32 位 CPU 编译的。对于 this(unsigned) int 通常有 32 位,而(unsigned) long 至少有 32 位(必需)。因此,操作没有环绕。
注意:对于 PC,操作数转换为int,而不是unsigned int。这是因为int 已经可以代表unsigned short 的所有值; unsigned int 不是必需的。如果操作结果溢出signed int!
,这可能会导致意外(实际上:
实现定义)行为
如果您需要定义大小的类型,请参阅 stdint.h(自 C99 起)以获取 uint16_t、uint32_t。这些是typedefs 类型,具有适合您的实现的大小。
您还可以将其中一个操作数(不是整个表达式!)转换为结果的类型:
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 的转换排名比类型转换大,这可能会将其值截断为较小的类型。虽然这很容易避免,因为所有类型在编译时都是已知的(静态类型),但另一种方法是乘以所需类型的 1:
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