提问人:toxic 提问时间:11/13/2023 最后编辑:Vlad from Moscowtoxic 更新时间:11/19/2023 访问量:172
C++ 通常的算术转换:cpp.sh 与 Godbolt 不同的结果 - 谁错了?
c++ usual arithmetic conversions: cpp.sh vs godbolt different results - who is wrong?
问:
以下是标准算术转换的简单展示:
// Example program
#include <iostream>
using namespace std;
int main()
{
unsigned long a = 0;
int b = 1;
long long c = -1;
cout << (c < a*b) << endl;
}
Godbolt 返回 0,cpp.sh 返回 1。
标准(https://en.cppreference.com/w/cpp/language/usual_arithmetic_conversions) 说:
第 4 阶段 ...
- 如果U的整数转换秩大于或等于S的整数转换秩,则C为U。
- 否则,如果 S 可以表示 U 的所有值,则 C 为 S。
- 否则,C 是对应于 S 的无符号整数类型。
哪个编译器是错误的,为什么?
答:
结果取决于是否等于 。也就是说,该类型是否可以表示该类型的所有正值。sizeof( unsigned long )
sizeof( long long )
long long
unsigned long
如果它不能表示该类型的值,则该表达式的操作数的公共类型unsigned long
(c < a*b)
将由于通常的算术转换,结果表达式产生值,因为转换为类型的非常大的正值。unsigned long long
false
-1
unsigned long long
否则,如果表达式的操作数的公共类型是 因为如上所述小于并且该类型可以表示该类型的所有正值,则表达式将产生值 。long long
sizeof( unsigned long )
sizeof( long long )
long long
unsigned long
true
尝试输出表达式和所用系统的值。sizeof( unsigned long )
sizeof( long long )
此外,您还可以运行以下测试程序。
#include <iostream>
#include <type_traits>
int main()
{
unsigned long x;
long long y;
std::cout << "sizeof( unsigned long ) = "
<< sizeof( unsigned long ) << '\n';
std::cout << "sizeof( long long ) = "
<< sizeof( long long ) << '\n';
std::cout << std::boolalpha;
std::cout << "std::is_same_v<decltype( y + x ), long long> is "
<< std::is_same_v<decltype( y + x ), long long> << '\n';
std::cout << "std::is_same_v<decltype( y + x ), unsigned long long> is "
<< std::is_same_v<decltype( y + x ), unsigned long long> << '\n';
}
例如,使用 MS VS C++,得到的结果是
sizeof( unsigned long ) = 4
sizeof( long long ) = 8
std::is_same_v<decltype( y + x ), long long> is true
std::is_same_v<decltype( y + x ), unsigned long long> is false
在 Godbolt 上使用 clang,得到的结果是
sizeof( unsigned long ) = 8
sizeof( long long ) = 8
std::is_same_v<decltype( y + x ), long long> is false
std::is_same_v<decltype( y + x ), unsigned long long> is true
来自 C++17 标准(8 个表达式)
11 许多二进制运算符需要算术或 枚举类型导致转换和产生类似结果类型 道路。目的是产生一个通用类型,这也是 结果。这种模式称为通常的算术转换, 其定义如下:
...
(11.5.4) — 否则,如果带有符号整数的操作数类型 type 可以表示操作数类型的所有值,其中 无符号整数类型,无符号整数类型的操作数应为 转换为具有有符号整数类型的操作数类型。
(11.5.5) — 否则,两个操作数都应转换为 与操作数类型相对应的无符号整数类型,其中 有符号整数类型。
在 MS VS C++ 中,对于类型的对象,并且由于类型具有相等的大小,因此可能会出现类似的问题。long
unsigned int
请注意,在 C++ 和 C 中,标准整数类型的大小都不是固定的,除了类型,其大小根据定义是相等的。char
signed char
unsigned char
1
为了在程序中获得预期的结果,您可以编写例如
std::cout << (c < static_cast<long long>( a*b ) ) << std::endl;
或者,如果您的编译器支持 C++20,则可以使用 header 中声明的标准函数。下面是一个演示程序:std::cmp_less
<utility>
#include <iostream>
#include <utility>
int main()
{
long long x = -1;
unsigned long y = 0;
std::cout << "sizeof( long long ) = " << sizeof( long long ) << '\n';
std::cout << "sizeof( unsigned long ) = " << sizeof( unsigned long ) << '\n';
std::cout << std::boolalpha;
std::cout << "( x < y ) is " << ( x < y ) << '\n';
std::cout << "std::cmp_less( x, y ) is " << std::cmp_less( x, y ) << '\n';
}
程序输出可能如下所示
sizeof( long long ) = 8
sizeof( unsigned long ) = 8
( x < y ) is false
std::cmp_less( x, y ) is true
评论
b