【问题标题】:gcc 7.2: warning: left shift count >= width of typegcc 7.2:警告:左移计数> =类型宽度
【发布时间】:2023-04-08 22:09:01
【问题描述】:

即使我将每个正确的操作数都转换为 unsigned long long,警告仍然存在。 uint8_t << uint64_t 不应该有这样的隐式转换:(uint64_t) uint8_t << uint64_t

This answer 建议我可以提升任一操作数,整个表达式将转换为 unsigned long long,但这可能是错误的。

bool dgBioReadU64LE(DgBioFile *file, uint64_t *x) {
    uint8_t u[8];
    if (!dgBioReadU8v(file, LEN(u), u)) return false;
    *x = u[0]|(u[1]<<8ULL)|(u[2]<<16ULL)|(u[3]<<24ULL)|(u[4]<<32ULL)|(u[5]<<40ULL)|(u[6]<<48ULL)|(u[7]<<56ULL);
    return true;
}

bool dgBioReadU64BE(DgBioFile *file, uint64_t *x) {
    uint8_t u[8];
    if (!dgBioReadU8v(file, LEN(u), u)) return false;
    *x = u[7]|(u[6]<<8ULL)|(u[5]<<16ULL)|(u[4]<<24ULL)|(u[3]<<32ULL)|(u[2]<<40ULL)|(u[1]<<48ULL)|(u[0]<<56ULL);
    return true;
}

【问题讨论】:

    标签: c gcc


    【解决方案1】:

    uint8_t &lt;&lt; uint64_t 不应该有这样的隐式转换:(uint64_t) uint8_t &lt;&lt; uint64_t

    TL;DR - 不,移位运算符很特殊。

    完整答案

    您所描述的行为(本质上是匹配操作数类型)在 C 标准中称为 usual arithmetic conversions1

    我们看到该标准对许多运算符(例如对于加法运算符)强制执行此操作:

    [6.5.6] 如果两个操作数都具有算术类型,则对它们执行通常的算术转换。

    但是,我们在按位移位运算符的等效部分中没有看到这样的短语。我们最接近的是:

    [6.5.7] 整数提升在每个操作数上执行。结果的类型是提升的左操作数的类型。

    但是,integer promotionsa different thing - 他们说(基本上)任何小于[unsigned] int 的类型都将转换为[unsigned] int

    所以编译器在这里发出警告是正确的。 (我相信你可以猜到,解决方案是对左侧操作数执行显式强制转换;)


    1. 出于此答案的目的,我正在考虑 C11(特别是 N1570)规范。行为至少与 C99 相同。

    【讨论】:

    【解决方案2】:

    这个答案表明我可以提升任一操作数,整个表达式将强制转换为 unsigned long long,但这可能是错误的。

    是的。

    标准说:

    6.5.7 移位运算符
    对每个操作数执行整数提升。结果的类型是提升的类型 左操作数。如果右操作数的值为负数或为 大于或等于提升的左操作数的宽度,则 行为未定义

    这意味着您需要转换左边的操作数,转换右边的操作数不会在这里改变任何东西。

    【讨论】:

      【解决方案3】:

      您链接的答案具有误导性。似乎建议您可以扩大 either 操作数,它会触发将另一个操作数自动转换为常见(更宽)类型。对于 C 中的大多数二元运算符都是如此。但是,对于移位运算符则不是这样。

      在这方面,C 中的移位运算符实际上是 特殊的。他们的行为不对称。将右操作数的类型更改为更宽的类型不会触发将左操作数转换为右操作数的类型,并且不会影响结果类型。结果类型始终由左操作数的(可能提升的)类型定义。

      在您的情况下,您特别需要将 left 操作数转换为 unsigned long long 类型。

      【讨论】:

        【解决方案4】:

        左右移位运算符的结果与左操作数的类型相同。所以右操作数的类型不会影响结果的类型。

        您需要将左操作数强制转换为 unsigned long long 以获得所需的结果。

        【讨论】:

          【解决方案5】:

          即使我将每个正确的操作数都转换为 unsigned long long, 警告仍然存在。 uint8_t &lt;&lt; uint64_t 不应该有隐含的 像这样投射:(uint64_t) uint8_t &lt;&lt; uint64_t?

          没有。

          这个答案表明我可以提升任一操作数和 整个表达式将转换为 unsigned long long,但它可能是 错了。

          我明白您为什么认为链接的答案这么说,但它演示了使用 unsigned long long 类型的常量作为 left 操作数。那部分是正确的。就答案而言,如果在右侧使用更宽的类型也可以达到同样的效果,这是一种误导。

          标准规定:

          对每个操作数执行整数提升。方式 结果是提升的左操作数的结果。如果值 右操作数为负数或大于等于宽度 提升的左操作数的行为未定义。

          (C2011, 6.5.7/3)

          请注意,结果表达式的类型仅由左侧操作数的类型决定,并且它是右侧操作数相对于左侧(提升)类型的可以触发此特定不确定性规定的操作数。

          您正在左移uint8_t。整数提升将在左操作数上执行,产生int 类型的值。假设您的ints 是 32 位宽,左移 32 位或更多位会产生未定义的行为,并且在某些情况下,您可能会因为它是 signed 而得到额外的未定义行为,不是无符号整数。

          通过转换 left 操作数来纠正这个问题。例如,(uint64_t) u[7] &lt;&lt; 56

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-06-22
            • 2020-06-24
            • 1970-01-01
            • 2023-01-12
            相关资源
            最近更新 更多