[英]c = a + b and implicit conversion
使用我的编译器, 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
首先,您应该知道在C中标准类型没有标准整数类型的特定精度(可表示值的数量)。 它只需要每种类型的最小精度。 这些导致以下典型的位大小 , 标准允许更复杂的表示:
char
:8位 short
:16位 int
:16(!)位 long
:32位 long long
(自C99起):64位 注意:实现的实际限制(这意味着一定的精度)在limits.h
中给出。
其次,执行操作的类型由操作数的类型决定,而不是赋值左侧的类型(因为赋值也只是表达式)。 为此,上面给出的类型按转换等级排序 。 排名小于int
操作数首先转换为int
。 对于其他操作数,具有较小等级的操作数将转换为另一个操作数的类型。 这些是通常的算术转换 。
您的实现似乎使用16位unsigned int
,其大小与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
,则可能导致意外(实际上是: 实现定义 )行为!
如果需要定义大小的类型,请参阅uint16_t
, uint32_t
stdint.h
(自C99起)。 这些是具有适合您的实现大小的类型的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
a + b
将被计算为unsigned int
(分配给unsigned long
的事实不相关)。 C标准的任务,这个总和将环绕模“一加最大的无符号的可能”。 在您的系统上,看起来unsigned int
是16位,因此结果以模数65536计算。
在另一个系统上,它看起来像int
和unsigned int
更大,因此能够容纳更大的数字。 现在发生的事情非常微妙(承认@PascalCuoq):因为unsigned short
所有值都可以在int
中表示, a + b
将被计算为int
。 (只有当short
和int
具有相同的宽度时,或者以某种其他方式,某些unsigned short
值不能表示为int
并且sum将被计算为unsigned int
。)
虽然C标准没有为unsigned short
或unsigned int
指定固定大小,但是您的程序行为是明确定义的。 请注意,对于签名类型,情况并非如此。
最后,您可以使用大小类型uint16_t
, uint32_t
等,如果编译器支持,则保证具有指定的大小。
在C中, char
, short
(及其未签名的couterparts)和float
应该被视为“存储”类型,因为它们旨在优化存储,但不是CPU喜欢的“本机”大小,它们永远不会用于计算 。
例如,当您有两个char
值并将它们放在表达式中时,它们首先转换为int
,然后执行该操作。 原因是CPU使用int
更好。 对于总是隐式转换为double
进行计算的float
也会发生同样的情况。
在你的代码中,计算a+b
是两个无符号整数的和; 在C中,没有办法计算两个无符号短路的总和......你可以做的是将最终结果存储在一个无符号短路中,由于模数学的性质,它将是相同的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.