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

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

本文关键字:和的 无符号整数 结果 类型 两个 GCC Clang 为什么      更新时间:2023-10-16

我正在为我的模拟器编写一些低级代码,其中涉及大量 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.1和任何编译器版本的Clang给出此错误(测试到Clang 5.0.0年(。链接到编译器资源管理器。

所以我的问题如下:

  • IMO,添加两个unsigned ints不应隐式转换为int。还是我的假设是错误的?
  • 为什么ClangGCC(直到 9.3(会产生不同的结果?标准是否规定了此操作的任何约束,还是由编译器供应商决定?
  • GCC 10.1有什么变化?为什么这个错误没有弹出GCC 10.1

IMO,添加两个无符号整数不应隐式转换为整数。还是我的假设是错误的?

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

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

最近有一个很好的问题,为什么促销是签署int:https://stackoverflow.com/a/62042330/2079303

为什么Clang和GCC(直到9.3(会产生不同的结果?

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

还是由编译器供应商决定?

诊断消息由编译器供应商自行决定(除非另有指定,否则程序格式不正确时需要诊断消息(。

GCC 10.1 中有哪些变化?为什么GCC 10.1没有弹出此错误?

也许他们认为警告在这种情况下没有用,并摆脱了它。

代码中没有添加两个unsigned int。实际上你添加了两个unsigned short,因为uint16_tunsigned short的typedef。

整数提升规则规定,任何比int窄的整数类型(显示为+的操作数(都会提升为int(而不是预期的unsigned int(。

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

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

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

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

我遇到了同样的警告,因为我做了一点转变:

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