gcc 7.2:警告:左移计数> =类型的宽度

时间:2017-12-18 16:36:48

标签: c gcc

即使我将每个正确的操作数转换为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;
}

5 个答案:

答案 0 :(得分:5)

  

不应该uint8_t << uint64_t有这样的隐式演员:(uint64_t) uint8_t << uint64_t

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

完整答案

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

我们看到标准要求许多运营商(例如添加剂运营商):

  

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

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

  

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

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

所以编译器在这里警告是正确的。 (而且我确定你可以猜到,解决方法是在左手操作数上执行显式转换;)

1.出于本回答的目的,我考虑了C11(具体为N1570)规范。行为至少可以追溯到C99。

答案 1 :(得分:4)

  

这个答案表明我可以提升任何一个操作数,整个表达式将转换为无符号长整数,但它可能是错误的。

是。

标准说:

  

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

这意味着您需要投射左操作数,投出正确的操作数不会在此处更改。

答案 2 :(得分:2)

你联系的答案是误导性的。似乎建议您可以扩展 操作数,它将触发另一个操作数自动转换为公共(更宽)类型。对于C中的大多数二元运算符来说都是如此。但是,对于移位运算符来说,情况并非如此。

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

在您的情况下,您必须将操作数转换为unsigned long long类型。

答案 3 :(得分:1)

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

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

答案 4 :(得分:1)

  

即使我将每个正确的操作数转换为unsigned long long,也是如此   警告仍然存在。 uint8_t << uint64_t不应该隐含   如下所示:(uint64_t) uint8_t << uint64_t

没有

  

这个答案表明我可以推广任何一个操作数和   整个表达式将转换为unsigned long long,但它可能会   错。

我明白为什么你认为链接的答案会说明这一点,但它会演示使用unsigned long long类型的常量作为 left 操作数。那部分是正确的。如果答案表明通过在右侧使用更广泛的类型可以实现同样的目的,则会产生误导。

标准规定:

  

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

C2011, 6.5.7/3

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

你左移uint8_t。整数提升将在左操作数上执行,从而产生类型int的值。假设您的int是32位宽,左移32位或更多位会产生未定义的行为,在某些情况下,您可能会因为 signed ,而不是unsigned int。

通过强制转换 left 操作数来纠正此问题。例如,(uint64_t) u[7] << 56