使用我的编译器,c
为54464(16位截断),d
为10176。
但使用gcc
时,c
为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
答案 0 :(得分:16)
首先,您应该知道在C中标准类型没有标准整数类型的特定精度(可表示值的数量)。每种类型只需要最小精度。这导致以下典型位大小,standard允许更复杂的表示:
char
:8位short
:16位int
:16(!)位long
:32位long long
(自C99起):64位注意:limits.h
中给出了实现的实际限制(这意味着一定的精度)。
其次,执行操作的类型取决于操作数的类型,而不是赋值左侧的类型(因为赋值也只是表达式)。为此,上面给出的类型按转换等级排序。排名小于int
的操作数首先转换为int
。对于其他操作数,具有较小等级的操作数将转换为另一个操作数的类型。这些是usual arithmetic conversions。
您的实现似乎使用与unsigned int
大小相同的16位unsigned short
,因此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;
或使用已知大小的类型:
#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
答案 1 :(得分:6)
a + b
将被计算为unsigned int
(分配给unsigned long
的事实不相关)。 C标准要求此总和将环绕模数&#34;一个加上最大的无符号可能&#34;。在您的系统上,它看起来像unsigned int
是16位,因此结果以模数65536计算。
在另一个系统上,int
和unsigned int
看起来更大,因此能够容纳更大的数字。现在发生的事情非常微妙(承认@PascalCuoq):因为unsigned short
中int
的所有值都可以表示,a + b
将int
计算。short
。 (仅当int
和unsigned short
宽度相同时,或者以某种其他方式,int
的某些值无法表示为unsigned int
,且总和将计算为{ {1}}。)
虽然C标准没有为unsigned short
或unsigned int
指定固定大小,但您的程序行为是明确定义的。请注意,对于签名类型,这是不 true。
作为最后一点,您可以使用大小类型uint16_t
,uint32_t
等,如果您的编译器支持,保证具有指定的大小。
答案 2 :(得分:3)
在C中,char
,short
(及其未签名的couterparts)和float
类型应被视为&#34;存储&#34;类型,因为它们旨在优化存储,但不是&#34; native&#34; CPU喜欢的大小,从不用于计算。
例如,当您有两个char
值并将它们放在表达式中时,它们首先转换为int
,然后执行操作。原因是CPU使用int
更好地工作。对于总是隐式转换为float
进行计算的double
,情况也是如此。
在你的代码中,计算a+b
是两个无符号整数的总和;在C中,无法计算两个无符号短路的总和......你可以做的是将最终结果存储在无符号短路中,这要归功于模数学的特性,会是一样的。