我最近写了一些代码,实际上是用来测试其他代码的,我偶然发现了一个令人惊讶的整数提升的例子。下面是最小的测试案例:
#include <cstdint>
#include <limits>
int main()
{
std::uint8_t a, b;
a = std::numeric_limits<std::uint8_t>::max();
b = a;
a = a + 1;
if (a != b + 1)
return 1;
else
return 0;
}
令人惊讶的是,这个程序返回1。一些调试和预感显示,条件中的b + 1
实际上返回了256,而赋值中的a + 1
产生了预期值0。
C++17草案第8.10.6节(关于平等/不可转让运算符)规定:
如果两个操作数都是算术或枚举类型的,则对两个操作数执行通常的算术转换;如果指定的关系为真,则每个操作符应生成true;如果指定的关系为false,则为false。
什么是“通常的算术转换”,它们在标准中的定义是什么?我的猜测是,对于某些运算符,它们隐式地将较小的整数推广到int
或unsigned int
(这也得到了以下事实的支持:用unsigned int
替换std::uint8_t
会产生0,而且赋值运算符没有“通常的算术转换”子句)。
发布于 2019-05-10 01:01:52
你的猜测是正确的。C++中许多运算符(例如二进制运算和比较运算)的操作数受通常的算术转换的限制。在C++17中,通常的算术转换是在[expr]/11中指定的。我不想引用整个段落,因为它很大(你只需点击链接),但是对于积分类型,通常的算术转换归结为积分提升,然后有效地进行一些推广,如果初始积分提升后两个操作数的类型不一样,那么较小的类型就会转换为两者中较大的一个。积分提升基本上意味着任何小于int
的类型都将被提升为int
或unsigned int
,这两个类型中的任何一个都可以表示原始类型的所有可能值,这主要是导致示例中的行为的原因。
正如您自己已经知道的,在您的代码中,通常的算术转换发生在a = a + 1;
中,最明显的是,在您的if条件下。
if (a != b + 1)
…
其中,b
被提升为int
,使得b + 1
的结果为int
类型,而a
被提升为int
和!=
,因此发生在int
类型的值上,从而导致条件为真而不是假…。
https://stackoverflow.com/questions/56069291
复制相似问题