为什么两个无符号整数之和的结果类型在 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.1
和 Clang
的任何编译器版本给出此错误(测试直到 Clang 5.0.0
). Link 到编译器资源管理器。
所以我的问题如下:
- IMO,添加两个
unsigned ints
不应隐式转换为 int
。或者我的假设是错误的?
- 为什么
Clang
和 GCC
(直到 9.3)产生不同的结果?该标准是否对此操作规定了任何限制,还是由编译器供应商决定?
GCC 10.1
有什么变化?为什么 GCC 10.1
没有弹出此错误?
您的代码中没有添加两个 unsigned int
。事实上,您添加了两个 unsigned short
,因为 uint16_t
是 unsigned 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
个宽度:
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
我正在为我的模拟器编写一些低级代码,其中涉及大量 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
). Link 到编译器资源管理器。
所以我的问题如下:
- IMO,添加两个
unsigned ints
不应隐式转换为int
。或者我的假设是错误的? - 为什么
Clang
和GCC
(直到 9.3)产生不同的结果?该标准是否对此操作规定了任何限制,还是由编译器供应商决定? GCC 10.1
有什么变化?为什么GCC 10.1
没有弹出此错误?
您的代码中没有添加两个 unsigned int
。事实上,您添加了两个 unsigned short
,因为 uint16_t
是 unsigned 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
个宽度:
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