- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
对于我的编译器,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 中,标准类型对于标准整数类型没有特定的精度(可表示值的数量)。它只需要每种类型的最小精度。这些导致以下典型位大小,standard允许更复杂的表示:
char
:8位短
: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 编译的。因为这个(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
这样,使用强制转换或 a
(或 b
)中给定类型的较大等级。乘法将被任何合理的编译器消除。
另一种方法,甚至可以通过 typeof()
gcc 扩展来避免知道目标类型名称:
unsigned long c;
... many lines of code
c = ((typeof(c))1U * a) + b
关于c = a + b 和隐式转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31652572/
我是一名优秀的程序员,十分优秀!