4

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

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

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

这是我得到的回复:

“字符不会默认提升为整数。寄存器大小为 32 位,但作为编译器实现,可以将一行中的多个字节值打包到单个寄存器中。这并不总是可以预测的。唯一一次可以验证自动提升是在没有包裹结构的情况下将类型传递到调用堆栈,因为 C 标准正式需要调用堆栈内存中的 32 位值。大量 CPU 架构已经针对非 32 位值优化了汇编调用,所以没有在这种情况下,可以对 CPU 或编译器做出假设。”

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

4

4 回答 4

8

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

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

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

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

第 6.3.1.1.2 节描述了整数提升:

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

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

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

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

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

正如评论中提到的,整数提升也在函数调用参数上执行。

于 2015-09-03T19:21:42.127 回答
4

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

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

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

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

  • 这是什么“预测”?

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

  • 整数提升无关struct

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

  • 他说一切都需要是 32 位的,但由于 CPU
    也可以处理其他大小,所以不能肯定地说什么?现在怎么办?

...

于 2015-09-03T19:12:38.650 回答
2

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

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

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

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

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

于 2015-09-03T19:10:41.030 回答
0

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

所以实际的 CPU 性能和不同的 CPU 风格对性能的影响比你想象的要小。这是优化器是否能找到一套像样的指令。

于 2015-09-03T23:43:15.133 回答