对无符号变量的算术运算会产生带符号的值,这是标准行为吗?

时间:2019-02-09 00:41:36

标签: c++ c

减去两个无符号变量,我希望得到无符号结果。我确实意识到会发生溢出,但是没关系,我实际上是指望它。 似乎并非需要将结果用于其他操作时。这是标准行为还是未定义行为?

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

2 个答案:

答案 0 :(得分:6)

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

在C11中,§6.3.1.8 Usual arithmetic conversions说:

  

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

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

有关“整数促销”的更多信息,请参见§6.3.1 Arithmetic operands§6.3.1.1 Boolean, characters, and integers

  

在可以使用intunsigned int的表达式中可以使用以下内容:

     
      
  • 具有整数类型(intunsigned int以外的整数类型的对象或表达式)的整数转换等级小于或等于intunsigned int的等级。
  •   
  • _Boolintsigned intunsigned int类型的位域。
  •   
     

如果int可以表示原始类型的所有值(受位字段的宽度限制),则该值将转换为int;否则,它将转换为unsigned int。这些被称为整数促销 58)所有其他类型都被整数促销保持不变。

“等级”一词在该部分中定义;这很复杂,但是基本上long的排名高于int,而int的排名高于char

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

答案 1 :(得分:5)

在算术中,比int窄的整数被提升为int,然后对它们进行算术以int类型进行。如果将结果存储为uint8_t或其他类型,则它将转换为该类型。但是,如果将其传递给printf,它将保留为int

在C中,用于实数的常规算术转换为:

  • 如果任一类型为long double,则另一种将转换为long double
  • 否则,如果其中一个为double,则另一个将转换为double
  • 否则,如果其中一个为float,则另一个将转换为float
  • 否则,对每个操作数执行整数提升。然后:
    • 如果两个操作数具有相同的时间,则不执行进一步的转换。
    • 否则,如果两个都为有符号或无符号,则较窄的 1 操作数将转换为较宽的操作数。
    • 否则,如果无符号操作数的宽度等于或大于另一个,则有符号操作数将转换为无符号类型。
    • 否则,如果有符号类型可以表示无符号类型的所有值,则将无符号操作数转换为有符号类型。
    • 否则,两个操作数都将转换为与有符号类型相对应的无符号类型。

整数促销为:

  • 如果类型比unsigned int 1 ,则不会更改。
  • 否则,如果int可以表示该类型的所有值,则该值将转换为int
  • 否则,该值将转换为unsigned int

脚注

1 C标准实际上使用了 rank 的技术分类,其中涉及更多详细信息。它会影响C实现,在这种实现中,除了仅带符号和无符号之外,多个整数类型可以具有相同的宽度。