考虑:
uint16_t x;
uint16_t y;
y = 0;
x = y - 1;
X将是一些疯狂的数字。 See other SO articles for the "why" and to learn about 2's compliment。
(我不是在问为什么会这样。我知道为什么。我在问其他问题。请继续阅读。)
如果打开gcc标志-Wconversion,则上面这段完全有效的C代码将抛出conversion to ‘uint16_t {aka short unsigned int}’ from ‘int’ may alter its value
,这是编译器试图帮助我们避免混乱。而且我倾向于在程序中启用此类警告,因为它们可以帮助我避免混乱。
但是,在现实情况下,我们可能使用unsigned int作为计数器并希望减去1而不得到此警告。另外,我们希望将其用作计数器,将其降低为零,然后停止。要在C语言中执行此操作,我们必须进行某种检查。但是,这种检查代码将违反gcc警告。
就像上面的代码一样。
我想要我的蛋糕,我也想吃它。
对此有什么优雅的解决方案?
答案 0 :(得分:7)
将y-1
强制转换为uint16_t
。 GCC并不是在抱怨减法,而是在将表达式的结果分配给(现在更小的)类型后,您可能会丢失信息。 (在表达式中使用整数类型时,整数操作数将被默默地提升为int,当类型较小并且可以在不损失精度的情况下适合带符号的int。)
答案 1 :(得分:5)
在此代码中
x = y - 1;
根据C标准,自变量y
受"default integer promotion"的约束。
这意味着y - 1
是一个int
值,然后该int
的结果通过截断被分配回了uint16_t
。这就是海湾合作委员会所抱怨的。
解决问题的方法是将结果显式转换为uint16_t
:
x = ( uint16_t ) ( y - 1 );
完整说明可在6.3.1.1 Boolean, characters, and integers中找到:
每个整数类型的整数转换等级定义如下:
- 即使两个符号整数类型具有相同的表示形式,也不应具有相同的等级。
- 有符号整数类型的等级应大于精度较低的任何有符号整数类型的等级。
- long long int的等级应大于long int的等级,后者应大于int的等级,该等级应为 大于short int的等级,该等级应大于 签名字符的等级。
- 任何无符号整数类型的等级应等于相应的有符号整数类型的等级(如果有)。
- 任何标准整数类型的等级应大于宽度相同的任何扩展整数类型的等级。
- char的等级应等于有符号的char和未签名的char的等级。
- _Bool的等级应小于所有其他标准整数类型的等级。
- 任何枚举类型的等级应等于兼容整数类型的等级(见6.7.2.2)。
- 相对于另一种具有相同精度的扩展有符号整数类型,任何扩展的有符号整数类型的等级为 实施定义的,但仍然要遵守其他规则 确定整数转换排名。
- 对于所有整数类型T1,T2和T3,如果T1的等级高于T2,并且T2的等级高于T3,则T1的等级高于T3。
以下表达式可以在表达式中使用int或unsigned 可以使用int:
- 具有整数类型(整数或无符号整数)的对象或表达式,其整数转换等级小于或等于 int和unsigned int的等级。
- _Bool,int,signed int或unsigned int类型的位字段。
如果一个int可以表示原始类型的所有值(受限 通过宽度,对于位字段),将值转换为int; 否则,它将转换为unsigned int。这些被称为 整数促销。所有其他类型均按整数不变 促销。
整数促销保留包括符号在内的价值。如前所述 之前,是否将“普通”字符视为已签名 实施定义。
答案 2 :(得分:3)
表达式:
x = y - 1;
受整数提升的影响,如C standard的第6.3.1p2节所述:
以下表达式可以在表达式中使用int或unsigned 可以使用int:
- 具有整数类型(整数或无符号整数)的对象或表达式,其整数转换等级小于或等于 int和unsigned int的等级。
- _Bool,int,signed int或unsigned int类型的位字段。
如果一个int可以表示原始类型的所有值(受 宽度(对于位字段),将值转换为int; 否则,它将转换为unsigned int。这些被称为 整数促销。所有其他类型均按整数不变 促销。
由于y
的类型为uint16_t
,其等级比int
低,因此它被提升为int
。然后将其减去常数1,常数1的类型也为int
,因此y - 1
的结果为类型int
。然后,将该值分配给排名低于uint16_t
的{{1}},这就是收到警告的原因。
您需要将整个右侧投射到int
以避免发出警告:
uint16_t
请注意,仅转换x = (uint16_t)(y - 1);
运算符的一个或两个操作数是不够的,因为一旦在整数运算符中使用了低于-
的秩的值,整数提升规则仍将生效。表达。