使用我的编译器,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;是否有办法对这些案件发出警报?
对以下事项发出警告:
void foo(unsigned long a);
foo(a+b);但并没有警告:
unsigned long c = a + b发布于 2015-07-27 12:16:40
首先,您应该知道,在C中,标准类型对于标准整数类型没有特定的精度(可表示值的数目)。它只需要对每种类型的最小精度。这些结果导致了以下典型的位大小,标准允许更复杂的表示:
char:8位short:16位int:16 (!)比特long:32位long long (自C99):64位注意:在limits.h中给出了实现的实际限制(这意味着一定的精度)。
第二,执行操作的类型取决于操作数的类型,而不是赋值左侧的类型(因为赋值也只是表达式)。为此,上面给出的类型按转换等级排序。秩小于int的操作数首先被转换为int。对于其他操作数,级别较小的操作数转换为另一个操作数的类型。这些是常用算术变换。
您的实现似乎使用了与unsigned short相同大小的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的转换级别大于类型广播,则可能会将其值截断为较小的类型。虽然可以很容易地避免这种情况,因为所有类型都是在编译时已知的(静态类型),另一种方法是用所需类型中的一个进行乘法:
unsigned long c = ((unsigned long)1U * a) + b这样,就可以使用cast或a (或b)中给出的类型的更大级别。任何合理的编译器都会消除乘法运算。
另一种方法,即使不知道目标类型名称,也可以使用typeof() gcc扩展来完成:
unsigned long c;
... many lines of code
c = ((typeof(c))1U * a) + bhttps://stackoverflow.com/questions/31652572
复制相似问题