【问题标题】:Subtracting unsigned ints in C and getting -Wconversion warnings减去 C 中的无符号整数并获得 -Wconversion 警告
【发布时间】:2018-08-24 16:46:53
【问题描述】:

考虑:

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 警告。

就像上面的代码。

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

什么是优雅的解决方案?

【问题讨论】:

  • 使用显式转换?
  • 只是不要使用窄类型进行算术运算。它们存在的唯一原因是节省存储空间。
  • @EugeneSh.NOPE.... 使用正确的无符号文字(将 U 附加到文字字符串)使用显式强制转换将使警告静音,但不会避免有符号和无符号转换,给出一个可能改变的结果。

标签: c gcc


【解决方案1】:

y-1 显式转换为uint16_t。 GCC 不是在抱怨减法,而是在将表达式的结果分配给(现在更小的)类型后,您可能会丢失信息。 (当在表达式中使用整数操作数时,当类型更小并且可以放入有符号的 int 而不会丢失精度时,整数操作数会被默默地提升为 int。)

【讨论】:

  • 实际上,即使是无符号的窄类型也会被提升为int,所以你应该省略最后半句。
【解决方案2】:

在这段代码中

x = y - 1;

根据 C 标准,参数 y 受制于 "default integer promotion"

这意味着y - 1 是一个int 值,然后通过截断将int 结果分配回uint16_t。这就是 GCC 所抱怨的。

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

x = ( uint16_t ) ( y - 1 );

完整解释可见6.3.1.1 Boolean, characters, and integers

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

  • 任何两个有符号整数类型都不应具有相同的等级,即使它们具有相同的表示形式。
  • 有符号整数类型的等级应大于任何精度较低的有符号整数类型的等级。
  • long long int 的等级要大于 long int 的等级,long int 的等级要大于 int 的等级,应为 大于 short int 的秩,应大于 有符号字符的等级。
  • 任何无符号整数类型的等级应等于相应有符号整数类型的等级(如果有)。
  • 任何标准整数类型的秩都应大于任何具有相同宽度的扩展整数类型的秩。
  • 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; 否则,它将转换为无符号整数。这些被称为 整数促销。所有其他类型都不变整数 促销活动。

整数促销保留价值,包括符号。正如所讨论的 早些时候,“普通”字符是否被视为已签名是 实现定义。

【讨论】:

    【解决方案3】:

    表达式:

    x = y - 1;
    

    整数提升的影响,详见C standard 的第 6.3.1p2 节:

    以下内容可以用在表达式中,只要是 int 或 unsigned 可以使用 int:

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

    如果一个 int 可以表示原始类型的所有值(受 宽度,对于一个位域),该值被转换为一个 int; 否则,它将转换为无符号整数。这些被称为 整数促销。所有其他类型都不变整数 促销活动。

    因为y 属于uint16_t 类型,其等级低于int,所以它被提升为int。然后减去常数 1,它也有 int 类型,所以 y - 1 的结果是 int 类型。然后将该值分配给等级低于intuint16_t,这就是您收到警告的原因。

    您需要将整个右侧转换为 uint16_t 以避免出现警告:

    x = (uint16_t)(y - 1);
    

    请注意,仅强制转换 - 运算符的一个或两个操作数是不够的,因为一旦在表达式中使用了低于 int 的 rank 值,整数提升规则仍然会生效。

    【讨论】:

    • x = (uint16_t)(y - 1);完全 编写时编译的内容 x = y - 1; 缺少警告是由于 splicit cast,但问题仍然存在,请参阅我的回答解释一下。
    猜你喜欢
    • 2015-08-13
    • 1970-01-01
    • 2015-06-30
    • 2011-01-22
    • 2011-07-16
    • 2023-03-18
    • 1970-01-01
    • 2014-08-03
    • 1970-01-01
    相关资源
    最近更新 更多