首先,您应该知道在C语言中,标准类型没有特定的精度(可表示值的数量)用于标准整数类型。它只需要每种类型的最小精度。这些导致以下典型的位大小,
标准允许更复杂的表示:
char
: 8位
short
: 16位
int
: 16 (!)位
long
: 32位
long long
(自C99以来):64位
注意:一个实现的实际限制(意味着一定的精度)在
limits.h
中给出。
其次,操作的类型由操作数的类型决定,而不是赋值左侧的类型(因为赋值也只是一个表达式)。为此,上述给出的类型按照
转换等级进行排序。比
int
等级低的操作数首先会被转换为
int
。对于其他操作数,等级较低的操作数将被转换为另一个操作数的类型。这些是
通常的算术转换。
您的实现似乎使用了与
unsigned short
相同大小的16位
unsigned int
,因此
a
和
b
都被转换为
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_t
,
uint32_t
。这些是用于您的实现的适当大小的类型的
typedef
。您也可以将其中一个操作数(不是整个表达式!)强制转换为结果类型:
unsigned long c = (unsigned long)a + b;
或者,使用已知大小的类型:
...
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