当我编译代码的时候
typedef long long unsigned myuint64;
myuint64 a[2] = {18446744073709551615, 18446744073709551615}; // error on 32bit gcc
myuint64 b = 18446744073709551615; // no error
在带有ARM 32-GCC的Godbolt上,我得到错误
(3 warnings for "integer constant is so large that it is unsigned")
error: narrowing conversion of '-1' from 'long long int' to 'myuint64' {aka 'long long unsigned int'} [-Wnarrowing]
6 | myuint64 a[2] = {18446744073709551615, 18446744073709551615};
| ^~~~~~~~~~~~~~~~~~~~
error: narrowing conversion of '-1' from 'long long int' to 'myuint64' {aka 'long long unsigned int'} [-Wnarrowing]
6 | myuint64 a[2] = {18446744073709551615, 18446744073709551615};
(The number是uint 64的最大值)
我在使用ARM 64-GCC(或32 bit/64 bit Clang或32 bit/64 bit MSVC)时不会出现此错误。但我得到的错误时,使用其他32位GCC变种的Godbolt。
为什么只有这些32位GCC编译器才会出现错误?
我可以用18446744073709551615u
来修复它。是否有其他方法可以修复此错误而不更改代码,也不禁用此警告/错误(-Wno-narrowing
)?
1条答案
按热度按时间6ie5vjzr1#
你的代码是否编译取决于
__int128
是否存在,它只存在于ARM64 GCC。原因是,当你编写一个没有后缀的 * integer-literal * 时,比如u
,编译器会从这个列表中选择一个类型,直到该值符合以下类型之一:int
long int
long long int
__int128
您的值
18446744073709551615
等于264 - 1,因此64位有符号整数不能满足它,但__int128
可以。让我们来看看ARM64 gcc给你的integer字面量的类型:
警告消息与实际情况不符:您可以看到编译器输出一个接受
__int128
的函数,这是您的文字类型。使用ARM gcc(32位)编译的相同代码产生:
在这种情况下,文字太大,无法容纳任何类型,
__int128
不存在(尝试使用它会产生编译器错误),GCC选择将其视为警告,而不是错误。为什么你的代码只适用于64位编译器
GCC错误地选择
long long
作为您的文字,即使这段代码格式不佳,并将其转换为-1
。myuint64
是无符号的,因此它不能表示-1
,并且列表初始化不允许收缩转换。结果,我们得到一个编译器错误。在64位编译器上使用
__int128
,这不是问题,因为我们可以很好地将__int128(18446744073709551615)
转换为myuint64(18446744073709551615)
。总结
您已经发现了一个不寻常的情况,其中编译器诊断实际上是违反直觉的。忽略这些诊断并了解涉及
__int128
即可发现问题。我不知道你可以使用任何编译器标志来修复这个问题,所以我建议使用
u
后缀作为字面量,在这种情况下编译器可以选择:unsigned int
unsigned long int
unsigned long long int
__uint128