【问题标题】:Are chars automatically promoted in C expressions?字符是否在 C 表达式中自动提升?
【发布时间】:2015-11-29 18:04:54
【问题描述】:

我向我的一位同事发表了声明,内容是:

“在 C 表达式中,字符会自动提升为整数,这对性能来说很好,因为 CPU 在其自然字长下运行速度最快。

我相信由于 char 的等级,char 提升行为在标准中的某处进行了说明。

这是我得到的回复:

"字符不默认提升为整数。寄存器大小 是 32 位的,但是可以将一行中的多个字节值打包成一个 单个寄存器作为编译器实现。这并不总是 预测性的。您唯一可以验证自动促销的时间是 类型在未包裹时传递到调用堆栈 结构,因为 C 标准正式需要 32 位值 调用栈内存。大量CPU架构已经优化 汇编要求非 32 位值,因此不能做出任何假设 关于这种情况下的 CPU 或编译器。”

我不确定谁是对的,以及该相信什么。事实如何?

【问题讨论】:

  • 因为 C 标准官方需要调用栈内存中的 32 位值谁说的?!
  • 这种“C 标准正式需要调用堆栈内存中的 32 位值”的说法完全是 BS。 C标准没有提到位数。它甚至没有谈论堆栈。
  • 您的同事似乎在使用“registry”这个词,而他的意思似乎是“注册”。注册表是 Windows 操作系统的一项功能,而不是 CPU。
  • @EugeneSh。事实上,C11 标准中根本没有出现字栈。
  • @RedAlert:chars 在传递给没有原型的函数、可变参数函数或具有char以外原型的函数时也会被转换:C11 草案标准,@ 987654323@.

标签: c++ c char int integer-promotion


【解决方案1】:

C 不需要堆栈或指定任何关于 32 位寄存器的内容。

整数提升的基本原理之一是CERT 所说:

执行整数提升是为了避免中间值溢出导致的算术错误。例如:

signed char cresult, c1, c2, c3;
c1 = 100;
c2 = 3;
c3 = 4;
cresult = c1 * c2 / c3;

请注意,并非所有运算符都会使其参数成为通常算术转换的主题,例如赋值运算符或强制转换运算符没有整数提升。

【讨论】:

    【解决方案2】:

    是的,具有多个chars 的表达式,例如加法等。 (但不是逗号运算符之类的东西)和其他一些事情是在提升值上完成的(提升到int)。参见例如。 N3797, §4.5

    关于你同事的说法,里面有很多错误的地方:

    • “注册表”(寄存器)的大小不是通常是 32 位,根本不是。

    • 如果一个字节有8位,当然32位的寄存器可以容纳多个字节,
      但这不相关,编译器不是它可能的原因。

    • 这是什么“预测”?

    • 关于标准和 32 位的位是完全错误的。

    • 整数提升与struct无关

    • 在标准中,没有“堆栈”。那个概念
      在现实中使用堆栈不是强制性的(正如其他人所说)。

    • 他说一切都需要 32 位,但作为 CPU
      也可以处理其他尺寸,没有什么可以肯定的?现在呢?

    ...

    【讨论】:

      【解决方案3】:

      字符在 C 表达式中自动提升为整数

      是的,他们是。 C99 第 6.3.1.8 节,常用算术转换

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

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

      第 6.3.1.1.2 节描述了整数提升:

      以下内容可以用在表达式中,只要是 int 或 unsigned 可以使用 int:

      • 具有整数类型的对象或表达式,其整数转换等级小于或等于 int 和无符号的等级 诠释。
      • _Bool、int、signed int 或 unsigned int 类型的位域

      如果一个 int 可以表示原始类型的所有值,则该值为 转换为 int;否则,它将转换为无符号整数。 这些被称为整数促销。所有其他类型均保持不变 通过整数促销。

      char 的等级小于或等于int 的等级,因此char 包含在此处。

      (作为脚注,提到整数提升仅作为通常算术转换的一部分应用于某些参数表达式,应用于一元 +-~ 的操作数,以及移位运算符的两个操作数)。

      如 cmets 中所述,整数提升也在函数调用参数上执行。

      【讨论】:

      • 整数提升也在函数调用参数上执行。
      • 赞成,但为了清楚起见,我认为您应该在 6.3.1.1 的引文中包含更多前面的文本:“以下内容可用于表达式中,只要可能是 int 或 unsigned int used: — 整数类型的对象或表达式,其整数转换等级小于或等于 int 和 unsigned int 的等级。" (包括前面定义的 char)
      • @mattnewport 是的,我同意提供一些背景信息很重要。没有介绍性文字,它看起来就像是凭空出现的。将其添加到我的答案中,感谢您的建议。
      【解决方案4】:

      从逻辑上讲,是的,所有操作都是在提升的值上执行的。然而,在 as-if 规则下,能够证明结果相同的编译器可以选择省略实际的提升。微不足道的if (ch==0) 需要将ch 提升为int,但实际上这根本不需要。当且仅ch 为零时,优化器可以很容易地看到(int)ch 为零。

      因此,实际的 CPU 性能和不同的 CPU 风格对性能的影响比您想象的要小;就是优化器能否找到一套像样的指令。

      【讨论】:

        猜你喜欢
        • 2016-05-22
        • 1970-01-01
        • 1970-01-01
        • 2012-10-28
        • 2014-11-18
        • 1970-01-01
        • 2023-01-26
        • 1970-01-01
        • 2017-11-18
        相关资源
        最近更新 更多