首页 > 解决方案 > 对无符号变量进行算术运算会产生有符号值,这是标准行为吗?

问题描述

减去两个无符号变量,我期望得到无符号结果。我确实意识到会发生溢出,但没关系,我实际上指望它。当结果需要在另一个操作中使用时,似乎情况并非如此。这是标准行为还是未定义行为?

uint8_t n1 = 255;
uint8_t z = 0;
uint8_t n = 1;
printf("n1 is %" PRIu8 "\n", n1);
printf("z - n is %" PRIu8 "\n", z - n);
printf("n1 < z: %s\n", n1 < z ? "yes" : "no");
printf("z - n < z: %s\n", z - n < z ? "yes" : "no");
printf("(uint8_t)(z - n) < (uint8_t)z: %s\n", (uint8_t)(z - n) < (uint8_t)z ? "yes" : "no");

输出:

n1 is 255
z - n is 255
n1 < z: no
z - n < z: yes
(uint8_t)(z - n) < (uint8_t)z: no

标签: c++c

解决方案


当变量的类型为 时uint8_t,它们都被提升为(有符号)int,然后在提升的值之间发生减法,产生一个(有符号)int值。这是强制性的行为。

在 C11 中,§6.3.1.8 通常的算术转换说:

许多期望算术类型的操作数的运算符会以类似的方式导致转换和产生结果类型。目的是确定操作数和结果的通用实数类型。对于指定的操作数,每个操作数都在不改变类型域的情况下转换为对应的实数类型是公共实数类型的类型。除非另有明确说明,否则公共实数类型也是结果的对应实数类型,如果操作数相同,则其类型域为操作数的类型域,否则为复数。这种模式称为通常的算术转换:

  • 首先,如果任一操作数的对应实类型为long double,则将另一个操作数转换为对应实类型为 的类型,而不改变类型域long double
  • 否则,如果任一操作数的对应实类型为double,则将另一个操作数转换为对应实类型为 的类型,而不改变类型域double
  • 否则,如果任一操作数的对应实类型为float,则将另一个操作数转换为对应实类型为 的类型,而不改变类型域float62)
  • 否则,对两个操作数都执行整数提升。然后将以下规则应用于提升的操作数:
    • 如果两个操作数具有相同的类型,则不需要进一步转换。
    • 否则,如果两个操作数都具有有符号整数类型或都具有无符号整数类型,则具有较小整数转换等级的类型的操作数将转换为具有较高等级的操作数的类型。
    • 否则,如果无符号整数类型的操作数的等级大于或等于另一个操作数类型的等级,则将有符号整数类型的操作数转换为无符号整数类型的操作数的类型。
    • 否则,如果有符号整数类型的操作数的类型可以表示无符号整数类型的操作数的所有值,则将无符号整数类型的操作数转换为有符号整数类型的操作数的类型。
    • 否则,两个操作数都转换为与带符号整数类型的操作数类型对应的无符号整数类型。

有关“整数提升”的更多信息,请参阅§6.3.1 算术操作数§6.3.1.1 布尔值、字符和整数

以下可以用在表达式中,只要可以使用intor unsigned int

  • 具有整数类型(除intor之外unsigned int)的对象或表达式,其整数转换等级小于或等于intand的等级unsigned int
  • _Bool, int,signed int或类型的位域unsigned int

如果 anint可以表示原始类型的所有值(受宽度限制,对于位域),则该值将转换为int; 否则,将其转换为unsigned int. 这些被称为整数促销58)整数提升不会改变所有其他类型。

术语“等级”在该部分中定义;它很复杂,但基本上,long排名高于int,并且int排名高于char

毫无疑问,C++ 中的规则略有不同,但最终结果基本相同。


推荐阅读