首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C=a+b与隐式转换

C=a+b与隐式转换
EN

Stack Overflow用户
提问于 2015-07-27 12:01:42
回答 3查看 1.6K关注 0票数 18

使用我的编译器,c为54464 (16位截断),d为10176。但是对于gccc是120000,d是600000。

真正的行为是什么?行为不明确吗?还是我的编译器是假的?

代码语言:javascript
运行
复制
unsigned short a = 60000;
unsigned short b = 60000;
unsigned long c = a + b;
unsigned long d = a * 10;

是否有办法对这些案件发出警报?

对以下事项发出警告:

代码语言:javascript
运行
复制
void foo(unsigned long a);
foo(a+b);

但并没有警告:

代码语言:javascript
运行
复制
unsigned long c = a + b
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 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,因此ab被转换为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_tuint32_t。这些是具有适合您的实现的适当大小的类型的typedef

您还可以强制转换一个操作数(不是整个表达式!)对结果的类型:

代码语言:javascript
运行
复制
unsigned long c = (unsigned long)a + b;

或者,使用已知大小的类型:

代码语言:javascript
运行
复制
#include <stdint.h>
...
uint16_t a = 60000, b = 60000;
uint32_t c = (uint32_t)a + b;

注意,由于转换规则,转换一个操作数就足够了。

更新(感谢@chux):

上面所示的铸造没有问题。但是,如果a的转换级别大于类型广播,则可能会将其值截断为较小的类型。虽然可以很容易地避免这种情况,因为所有类型都是在编译时已知的(静态类型),另一种方法是用所需类型中的一个进行乘法:

代码语言:javascript
运行
复制
unsigned long c = ((unsigned long)1U * a) + b

这样,就可以使用cast或a (或b)中给出的类型的更大级别。任何合理的编译器都会消除乘法运算。

另一种方法,即使不知道目标类型名称,也可以使用typeof() gcc扩展来完成:

代码语言:javascript
运行
复制
unsigned long c;

... many lines of code

c = ((typeof(c))1U * a) + b
票数 16
EN

Stack Overflow用户

发布于 2015-07-27 12:05:44

a + b将被计算为unsigned int (分配给unsigned long的事实与此无关)。C标准要求这个和将围绕模“1加上最大的无符号可能”。在您的系统上,它看起来像一个unsigned int是16位,所以结果是计算模块65536。

在另一个系统中,intunsigned int看起来更大,因此能够容纳更大的数字。现在发生的事情非常微妙(确认@PascalCuoq):因为unsigned short的所有值在int中都是可表示的,a + b将被计算为int。(只有当shortint具有相同的宽度,或者以其他方式不能将unsigned short的某些值表示为int时,才会将和计算为unsigned int。)

尽管C标准没有为unsigned shortunsigned int指定固定大小,但您的程序行为是很好定义的。但请注意,对于有符号类型,这是而不是 true。

最后,您可以使用uint16_tuint32_t等大小类型,这些类型如果得到编译器的支持,则保证具有指定的大小。

票数 6
EN

Stack Overflow用户

发布于 2015-07-27 16:26:44

在C中,charshort (及其无符号耦合器件)和float类型应该被视为“存储”类型,因为它们是为优化存储而设计的,但不是CPU所喜欢的“本地”大小,而从不用于计算

例如,当您有两个char值并将它们放在表达式中时,首先将它们转换为int,然后执行操作。原因是CPU在int中工作得更好。对于总是隐式转换为double以进行计算的double,也会发生同样的情况。

在您的代码中,计算a+b是两个无符号整数的和;在C中,无法计算两个无符号短路的和。您可以做的是将存储在一个没有签名的短消息中,最后结果,由于模块化数学的特性,它将是相同的。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31652572

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档