无符号整数会被提升为有符号整数吗?什么是表达式类型 (uint16_t)-1 * (uint16_t)-1

Do unsigned integers get promoted to signed? What is the type of expression (uint16_t)-1 * (uint16_t)-1

对于uint32_tuint64_t结果是预期的,但是对于uint8_tuint16_t的提升很奇怪。测试 c++14/c++17、gcc 和 clang,64 位 linux、sizeof(int) == 4.

#include <cstdint>    
using namespace std;

static_assert( uint8_t(-1)*uint8_t(-1) == 0xfe01);  // OK, but why not 1?
static_assert( uint8_t(-1)*uint8_t(-1) == 1);       // error: static assertion failed
static_assert( uint16_t(-1)*uint16_t(-1) == 1);     // error: static_assert expression is not an integral constant expression
static_assert( uint32_t(-1)*uint32_t(-1) == 1);     // OK
static_assert( uint64_t(-1)*uint64_t(-1) == 1);     // OK

下面的情况是std::uint16_t提升为int吗?

static_assert( uint16_t(-1)*uint16_t(-1) == uint16_t(uint16_t(-1)*uint16_t(-1)));

编译器消息是:

error: static_assert expression is not an integral constant expression
static_assert( uint16_t(-1)*uint16_t(-1) == uint16_t(uint16_t(-1)*uint16_t(-1)));

note: value 4294836225 is outside the range of representable values of type 'int'
static_assert( uint16_t(-1)*uint16_t(-1) == uint16_t(uint16_t(-1)*uint16_t(-1)));

更有趣的是,std::uint8_t 的相同断言是正确的,但是 失败了 :

static_assert( uint8_t(-1)*uint8_t(-1) == uint8_t(uint8_t(-1)*uint8_t(-1)));  //error: static_assert failed
static_assert( uint8_t(-1)*uint8_t(-1) == 0xfe01);  // does not fail

所以看起来 uint8_t 被提升为 uint16_t,但是 uint16_t 被提升为 signed intuint32_t 未升级 uint64_t.

任何人都可以告诉我为什么要进行这些促销活动,它是在标准中指定的,还是在不同的实现中有所不同?

it looks like uint8_t is promoted to uint16_t, but uint16_t is promoted to signed int. uint32_t is not promoted uint64_t.

不,那是不正确的。只有比 int 窄的类型才会被提升为 int,因此如果 int 的长度超过 16 位,则 uint8_tuint16_t 都将被提升为 int.

uint8_t(-1)*uint8_t(-1)的情况下,相当于0xFF*0xFF,结果为0xFE01

OTOH uint16_t(-1)*uint16_t(-1)0xFFFF*0xFFFF 相同,后者导致 0xFFFE0001 = 4294836225 并且超出了 int 的范围。签名溢出是未定义的行为

Do unsigned integers get promoted to signed?

这取决于与 int 的大小相关的操作数的大小。但是,是的,这是典型的。

static_assert( uint8_t(-1)*uint8_t(-1) == 0xfe01);  // OK, but why not 1?

它不是 1,因为 uint8_t(-1) 是 255 而 255*255 是 65025。在具有 32 位 int 的系统上,它在可表示的值范围内。

So it looks like uint8_t is promoted to uint16_t

没有。晋升总是发生在 int 或更高级别的整数。 uint8_t 可以在 int 是 16 位的系统上提升为 16 位类型,但是由于 uint8_t 的所有值都可以用带符号的 int 表示,这将是结果促销活动。

can differ on different implementations?

可表示值的范围因语言实现而异,这会影响提升的类型。

is it specified in standard

是的。

Do you know how to suppress the promotion to signed int, and get it promoted to unsigned int?

不,无法更改促销活动。但是您可以将该值显式转换为所需的类型,这样就没有提升。

算术运算符不接受小于 int 的类型作为参数。
对于较小的参数,应用积分提升到 int

这就是结果有些不一致的原因:

  • 当乘以小于 int 的类型时,你得到提升,
  • 当你不乘以较大的类型时(如果它们相同)。

确切的规则太复杂了,总结起来不会出错,但在那些 CppReference 文章中有描述: