为什么两个无符号整数之和的结果类型在 Clang 和 GCC 中不同

Why is the result type of a sum of two unsigned integers different in Clang and GCC

我正在为我的模拟器编写一些低级代码,其中涉及大量 16 位和 8 位无符号整数。我在我的项目中启用了 -Wconversion 警告,所有警告都被视为错误 (-Werror)。

考虑这个代码片段:

#include <cstdint>

int main ()
{
    uint16_t a = 4;
    uint16_t b = 6;
    uint16_t c = a + b;
}

直到 GCC 9.3 以 -std=c++17 -Wconversion -Werror 作为编译标志,给出以下错误:

<source>: In function 'int main()':
<source>:7:20: error: conversion from 'int' to 'uint16_t' {aka 'short unsigned int'} may change value [-Werror=conversion]
    7 |     uint16_t c = a + b;

      |                  ~~^~~

但是 相同的代码不会为 GCC 10.1Clang 的任何编译器版本给出此错误(测试直到 Clang 5.0.0). Link 到编译器资源管理器。

所以我的问题如下:

您的代码中没有添加两个 unsigned int。事实上,您添加了两个 unsigned short,因为 uint16_tunsigned short.

的类型定义

整数提升规则说任何比 int 窄的整数类型出现作为 + 的操作数被提升为 int (不是 unsigned int 你可能期待)。

所以涉及的步骤是将 (int)4 添加到 (int)6 得到 (int)10,然后将其分配回 c

您应该发现所有编译器都为 c 提供了正确的值,代码的行为是明确定义的。

-Wconversion 的行为更具争议性。由于这段代码是一个完美的例子,它经常对定义明确的代码发出警告。除了不使用标志或以某种方式包装误报(可能通过 #pragma 或函数调用)之外,没有明显的解决方案。

有些人确实希望看到针对此代码的警告,有些人则不想。 -Wconversion 生成警告的确切案例集在 gcc 中不断变化,因为人们提交错误报告说他们{不|不}希望看到针对某些特定案例的警告。

IMO, addition of two unsigned ints should not be implicitly converted to an int. Or is my assumption wrong?

这个假设没有错。 Unsigned int 永远不会隐式转换为 int。

但是在您的系统上,uint16_t 恰好是无符号 short int。假设 unsigned short int 没有隐式转换为 int 是错误的假设。它们在大多数系统上被提升为 int。

最近有一个很好的问题,关于为什么 promotion is to signed int:

Why Clang and GCC(until 9.3) produce different results?

他们没有。两者都将提升为 int。一个人根本没有警告转换。转换没有错误格式,因此不需要发出诊断。

or is it up to the compiler vendor to decide?

诊断消息由编译器供应商自行决定(除非另有说明,否则当程序格式错误时需要诊断消息)。

What changed in GCC 10.1? Why is this error not popping up for GCC 10.1?

也许他们认为警告在这种情况下没有用,因此将其删除。

我 运行 进入相同的警告进行位移:

uint8_t left_shift(uint8_t a, uint8_t b) { return a << b; }

启用 -Wconversion 警告的 Clang 输出:

<source>:10:53: warning: implicit conversion loses integer precision: 'int' to 'uint8_t' (aka 'unsigned char') [-Wimplicit-int-conversion]

uint8_t left_shift(uint8_t a, uint8_t b) { return a << b; }
                                           ~~~~~~ ~~^~~~

但是程序集列表没有显示其他人在此线程中提到的幻影 int 促销的迹象。全文仅使用 byte 个宽度:

https://godbolt.org/z/eSVaQW

left_shift(unsigned char, unsigned char):
        push    rbp
        mov     rbp, rsp
        mov     byte ptr [rbp - 1], dil
        mov     byte ptr [rbp - 2], sil
        movzx   eax, byte ptr [rbp - 1]
        movzx   ecx, byte ptr [rbp - 2]
        shl     eax, cl
        movzx   eax, al
        pop     rbp
        ret