在C中减去无符号整数并获取-Wconversion警告

010*_*101 4 c gcc

考虑:

uint16_t x;
uint16_t y;

y = 0;
x = y - 1;
Run Code Online (Sandbox Code Playgroud)

X将是一些疯狂的数字. 请参阅其他SO文章的"为什么",并了解2的赞美.

(我不是在问为什么会这样.我知道为什么.我在问别的.请继续阅读.)

如果你打开gcc标志-Wconversion,这个完全有效的C代码将抛出conversion to ‘uint16_t {aka short unsigned int}’ from ‘int’ may alter its value那就是编译器试图帮助我们不要搞砸了.而且我倾向于打开这样的警告,因为它们可以帮助我避免搞砸.

但是,有一些现实情况,我们可能会使用unsigned int作为计数器,并希望减去1并且不会得到此警告.此外,我们希望将其用作计数器,将其降至零,然后停止.要在C中执行此操作,我们必须进行某种检查.但是,这种检查代码会违反gcc警告.

像上面的代码一样.

我想要我的蛋糕,我也想吃它.

这个优雅的解决方案是什么?

Kit*_*ten 7

明确地投射y-1uint16_t.GCC抱怨的不是减法,而是在将表达式的结果分配给(现在更小)类型后,您可能会丢失信息.(当在表达式中使用时,积分操作数被静默地提升为int,当类型较小并且可以适合有符号的int而不会丢失精度.)


And*_*nle 5

在这段代码中

x = y - 1;
Run Code Online (Sandbox Code Playgroud)

根据C标准,该参数y"默认整数提升"的约束.

这意味着y - 1是一个int值,然后该int结果被分配回uint16_t通过截断.这就是海湾合作委员会所抱怨的.

解决问题的方法是将结果显式地转换为uint16_t:

x = ( uint16_t ) ( y - 1 );
Run Code Online (Sandbox Code Playgroud)

完整的解释可以在6.3.1.1布尔,字符和整数中找到:

每个整数类型都有一个整数转换等级,定义如下:

  • 没有两个有符号整数类型具有相同的等级,即使它们具有相同的表示.
  • 有符号整数类型的等级应大于精度较低的任何有符号整数类型的等级.
  • long long int的等级应大于long int的等级,该等级应大于int的等级,其应大于short int的等级,short rank应大于signed char的等级.
  • 任何无符号整数类型的等级应等于相应的有符号整数类型的等级(如果有的话).
  • 任何标准整数类型的等级应大于具有相同宽度的任何扩展整数类型的等级.
  • char的等级应等于signed char和unsigned char的等级.
  • _Bool的等级应小于所有其他标准整数类型的等级.
  • 任何枚举类型的等级应等于兼容整数类型的等级(见6.7.2.2).
  • 任何扩展有符号整数类型相对于具有相同精度的另一个扩展有符号整数类型的等级是实现定义的,但仍然受制于确定整数转换等级的其他规则.
  • 对于所有整数类型T1,T2和T3,如果T1具有比T2更大的秩并且T2具有比T3更大的秩,则T1具有比T3更大的秩.

如果可以使用int或unsigned int,则可以在表达式中使用以下内容:

  • 具有整数类型(int或unsigned int除外)的对象或表达式,其整数转换等级小于或等于int和unsigned int的等级.
  • _Bool,int,signed int或unsigned int类型的位字段.

如果int可以表示原始类型的所有值(由宽度限制,对于位字段),则该值将转换为int; 否则,它将转换为unsigned int.这些被称为整数促销.整数促销不会更改所有其他类型.

整数促销保留包括符号在内的值.如前所述,"普通"字符是否被视为已签名是实现定义的.