gpt4 book ai didi

c - 隐式类型提升规则

转载 作者:行者123 更新时间:2023-11-30 19:24:31 27 4
gpt4 key购买 nike

该帖子旨在用作有关C中隐式整数提升的FAQ,尤其是由通常的算术转换和/或整数提升引起的隐式提升。

示例1)
为什么这会给出一个奇怪的大整数而不是255?

unsigned char x = 0;
unsigned char y = 1;
printf("%u\n", x - y);


示例2)
为什么给出“ -1大于0”?

unsigned int a = 1;
signed int b = -2;
if(a + b > 0)
puts("-1 is larger than 0");


示例3)
为什么在上面的示例中将类型更改为 short可以解决问题?

unsigned short a = 1;
signed short b = -2;
if(a + b > 0)
puts("-1 is larger than 0"); // will not print


(这些示例适用于16位短的32位或64位计算机。)

最佳答案

C被设计为隐式和无声地更改表达式中使用的操作数的整数类型。在某些情况下,该语言会迫使编译器将操作数更改为更大的类型,或者更改其符号。

其背后的原理是为了防止算术期间意外溢出,而且还允许具有不同符号的操作数在同一表达式中共存。

不幸的是,隐式类型提升的规则弊大于利,以至于它们可能是C语言中最大的缺陷之一。这些规则通常对于普通C程序员来说甚至都不为人所知,因此会引起各种非常细微的错误。

通常情况下,您会看到程序员说“只需强制转换为x即可使用它”的情况-但他们不知道为什么。或者,这些bug表现为看似简单而直接的代码中罕见的间歇性现象。隐式提升在执行位操作的代码中尤其麻烦,因为在给定有符号操作数的情况下,C中大多数位操作符的行为都定义不明确。



整数类型和转换等级

C中的整数类型为charshortintlonglong longenum
_Bool / bool在类型促销中也被视为整数类型。

所有整数都有指定的转换等级。 C11 6.3.1.1,重点放在最重要的部分:


  每个整数类型都有一个整数转换等级,定义如下:
  —即使两个有符号整数类型具有相同的表示形式,也不应具有相同的等级。
  —有符号整数类型的等级应大于精度较低的任何有符号整数类型的等级。
  — long long int的等级应大于long int的等级,该等级应大于int的等级,该等级应大于short int的等级,后者应大于signed char的等级。 stdint.h
  —任何无符号整数类型的等级应等于相应的有符号整数类型的等级(如果有)。
  —任何标准整数类型的秩应大于相同宽度的任何扩展整数类型的秩。
  —字符的等级应等于有符号字符和无符号字符的等级。
  — _Bool的等级应小于所有其他标准整数类型的等级。
  —任何枚举类型的等级应等于兼容整数类型的等级(见6.7.2.2)。


int32_t中的类型也在这里排序,与它们在给定系统上对应的任何类型的排名相同。例如,在32位系统上,intint具有相同的等级。

此外,C11 6.3.1.1指定哪些类型被视为小整数类型(不是形式术语):


  在unsigned intint可能在的表达式中可以使用以下内容
  使用:
  
  —具有整数类型(unsigned intint除外)的对象或表达式,其整数转换等级小于或等于unsigned int_Bool的等级。


在实践中,这种有点神秘的文字意味着charshortint8_t(还有uint8_tint等)是“小整数类型”。如下所述,这些内容将以特殊方式处理并受到隐式提升。



整数促销

每当在表达式中使用小整数类型时,它都会隐式转换为始终带有符号的int。这称为整数提升或整数提升规则。

正式而言,规则说(C11 6.3.1.1):


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


这意味着,在大多数表达式中使用时,所有小的整数类型(无论是否带符号)都将隐式转换为(带符号的)unsigned short

该文本经常被误解为:“所有小的带符号整数类型都转换为带符号的int,而所有小的带符号整数类型都转换为无符号的int”。这是不正确的。这里的无符号部分仅意味着,例如,如果我们有一个int操作数,并且short在给定系统上恰好具有与unsigned short相同的大小,则unsigned int操作数将转换为short。在这种情况下,什么都没有真正发生。但是,如果int是小于int的类型,则始终将其转换为(带符号的)char,而不管短代码是带符号的还是无符号的!

整数提升引起的严酷现实意味着,在C中几乎无法对shortint之类的小字符执行任何操作。操作始终在unsigned char或更大的类型上进行。

这听起来像是胡说八道,但是幸运的是编译器被允许优化代码。例如,包含两个int操作数的表达式将把操作数提升为int,并将操作作为int进行。但是,可以预期,编译器可以优化表达式以实际以8位运算的形式执行。但是,问题来了:不允许编译器优化由整数提升引起的有符号性的隐式更改。因为编译器无法判断程序员是故意依赖隐式升级还是非故意的。

这就是为什么问题中的示例1失败的原因。两个无符号char操作数都提升为int类型,对该类型的x - y执行该操作,并且int的结果为-1类型。意味着我们得到的是255而不是预期的int。编译器可能会生成使用8位指令而不是printf("%u来执行代码的机器代码,但可能无法优化签名的更改。这意味着我们最终得到一个否定的结果,这反过来导致在调用unsigned char时产生一个怪异的数字。可以通过将操作结果转换回类型++来修复示例1。

除了sizeofif-else if运算符之类的一些特殊情况外,整数提升适用于C中几乎所有的运算,无论是否使用一元,二进制(或三进制)运算符。



通常的算术转换

每当在C中执行二进制运算(带有2个操作数的运算)时,运算符的两个操作数都必须具有相同的类型。因此,在操作数为不同类型的情况下,C强制将一个操作数隐式转换为另一操作数的类型。如何完成此操作的规则称为通常的人工转换(有时非正式地称为“平衡”)。这些在C11 6.3.18中指定:

(将此规则视为长的嵌套long double语句,可能更容易阅读:))


  6.3.1.8常规算术转换
  
  许多期望算术类型的操作数的运算符会导致转换并产生结果
  以类似的方式输入。目的是确定操作数的通用实型
  结果。对于指定的操作数,将转换每个操作数,而无需更改类型
  域,将其转换为对应的实型是普通实型的类型。除非
  除非另有明确说明,否则普通实型也是对应的实型
  结果,如果它们是相同的,则其类型域是操作数的类型域,
  否则就很复杂。这种模式称为通常的算术转换:
  
  
  首先,如果一个操作数的相应实型为long double,则将另一个操作数转换为相应实型为double的类型,而不会改变类型域。
  否则,如果任一操作数的相应实型为double,则将另一个操作数转换为相应实型为float的类型,而不会改变类型域。
  否则,如果任一操作数的对应实型为int,则另一个操作数将转换为对应实型为float的类型,而不会改变类型域。
  否则,将对两个操作数执行整数提升。然后
  以下规则适用于提升的操作数:
  
  
  如果两个操作数具有相同的类型,则无需进一步转换。
  否则,如果两个操作数都具有符号整数类型或都具有无符号
  整数类型,具有较小整数转换等级的类型的操作数为
  转换为具有更高等级的操作数的类型。
  否则,如果具有无符号整数类型的操作数的秩更大或
  等于另一个操作数类型的等级,然后是
  有符号整数类型将转换为无符号操作数的类型
  整数类型。
  否则,如果带符号整数类型的操作数的类型可以表示
  具有无符号整数类型的操作数类型的所有值,然后
  无符号整数类型的操作数将转换为
  带符号整数类型的操作数。
  否则,两个操作数都将转换为无符号整数类型
  对应于带符号整数类型的操作数的类型。
  
  


这里值得注意的是,通常的算术转换适用于浮点数和整数变量。如果是整数,我们还可以注意到,整数促销是从常规算术转换中调用的。之后,当两个操作数的秩至少为a + b时,运算符将被平衡为具有相同符号的相同类型。

这就是为什么示例2中的int给出奇怪结果的原因。这两个操作数都是整数,并且它们的排名至少为a,因此整数提升不适用。操作数的类型不同-unsigned intbsigned intb。因此,运算符unsigned int临时转换为类型short。在此转换过程中,它会丢失符号信息并最终产生较大的值。

在示例3中将类型更改为short的原因解决了该问题,这是因为int是小的整数类型。这意味着两个操作数都是整数,提升为带符号的int类型。整数提升后,两个操作数具有相同的类型( ),不需要进一步的转换。然后可以按预期在带符号的类型上执行该操作。

关于c - 隐式类型提升规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60243654/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com