【问题标题】:Explain integer comparison with promotion用提升解释整数比较
【发布时间】:2019-11-09 19:41:21
【问题描述】:

我试图了解整数提升和比较在 C++ 应用程序中的工作原理。

#include <cstdint>

int main(void)
{
    uint32_t foo  = 20;
    uint8_t a = 2;
    uint8_t b = 1;
    uint8_t c = 5;

    if(foo == b*c) {}

    if(foo == a) {}

    if(foo == a + c) {}

    if(foo == a + b*c) {}

    return 0;
}

仅在最后一次比较时,我会收到编译器警告:“有符号和无符号整数表达式之间的比较 [-Wsign-compare]”。

为什么这只发生在最后一种情况,而其他情况没有?

【问题讨论】:

  • b * ca + ca + b*c 都被提升为 ints。由于某种原因,您的编译器只对其中一个发出警告。
  • 哪个编译器,哪个版本,以及什么命令行开关?
  • @EricPostpischil,这可以在 g++ 6.3.0 中使用-Wall 重现。还有 g++ 7.4.0。

标签: c++ integer-promotion


【解决方案1】:

由于操作数的类型不同,因此会进行一组隐式转换以达到通用类型。

对于二元运算符(移位除外),如果提升的操作数 有不同的类型,另外一组隐式转换是 应用,像往常一样被称为arithmetic conversions,目标是 产生通用类型(也可以通过 std::common_type 类型访问 特质)

因为这里的整数类型整数转换适用于:

  • 如果任一操作数具有范围枚举类型,则不执行转换:另一个操作数和返回类型必须相同
    类型
    • 否则,如果任一操作数为 long double,则另一个操作数将转换为 long double
    • 否则,如果任一操作数为双精度,则另一个操作数将转换为双精度
    • 否则,如果任一操作数为浮点数,则将另一个操作数转换为浮点数
    • 否则,操作数为整数类型(因为bool、char、char8_t、char16_t、char32_t、wchar_t和无作用域枚举 在这一点上被提升)和积分转换是 应用来产生通用类型,如下:
    • 如果两个操作数都带符号或都无符号,则转换等级较低的操作数将转换为具有 更大的整数转换等级
    • 否则,如果无符号操作数的转换等级大于或等于有符号的转换等级 操作数,有符号操作数转换为无符号操作数
      操作数的类型。
    • 否则,如果有符号操作数的类型可以表示无符号操作数的所有值,则将无符号操作数转换为 有符号操作数的类型 否则,两个操作数都转换为 带符号操作数类型的无符号对应物。

同样的算术转换也适用于comparison operators

从这一切可以得出结论,因为rhs 都是uint8_t,通用类型将是int,然后由于rhsuint32_t== 运算符的通用类型将是uint32_t . 但由于某种原因,我不知道gcc 在 clang 进行最后一次转换时不要进行。请参阅godblot+ 运算符的gcc 类型转换@ 也可能发生警告是错误警告并且发生转换的情况,就像 + 操作员发生的那样。 看看clang如何看到最后一个if(cppinsights):

if(foo == static_cast<unsigned int>(static_cast<int>(a) + (static_cast<int> 
(b) * static_cast<int>(c))))

更新:

我在两个编译器生成的程序集中找不到差异,我同意 @M.M 所以,IMO 这是一个 gcc 错误。

【讨论】:

  • “不进行转换”和“不警告进行转换”是有区别的。该问题仅提供缺乏警告的证据,而不是未完成转换的证据。为了证明编译器实际上没有进行所需的转换,有必要检查输出代码(汇编程序等)。
  • @Peter 你是对的,必须检查程序集。也许警告是错误的警告。我编辑我的声明
  • 这不是大惊喜吗,未签名的被提升为已签名的?如果没有 C++ 律师学位,我很想说这是根本问题。
【解决方案2】:

这是一个编译器“错误”。详细说明:

  • 一般来说,有符号和无符号之间的比较依赖于实现定义的数量(类型的大小/范围)。例如,USHRT_MAX == -1 在常见的 16 位系统上为真,在常见的 32 位系统上为假。 “遗忘”的答案涉及到更多的技术细节。

  • 您的所有代码示例都定义良好,并且在所有(符合标准的)系统上表现相同。

这个警告的意图是双重的:

  1. 提醒您注意在其他系统上可能表现不同的代码。
  2. 提醒您代码可能不符合编码人员的预期。

但是,一般来说。编译器的静态分析要理清第一种情况可不是一件简单的事,更不用说比较主观的第二种情况了。

IMO 对您的代码的警告是一个错误,因为代码定义明确,没有什么可警告的。

我个人不启用此警告:我熟悉有符号-无符号比较的规则,并且更愿意避免破坏我的代码以抑制警告。

走向相反的极端,有些人宁愿在他们的代码中避免所有有符号-无符号比较,即使它是明确定义的;他们会认为这是一个错误,编译器没有警告您的前三个代码示例。

GCC 倾向于在警告方面犯错,但他们处于无法取悦所有人的境地。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-04
    • 2011-03-07
    • 1970-01-01
    • 2017-02-11
    • 2015-07-20
    • 1970-01-01
    • 2015-10-12
    • 1970-01-01
    相关资源
    最近更新 更多