从我从这个问题得到的答案来看,在从 C 执行算术运算时,C++ 似乎继承了转换为的这一要求short
。int
我能否请您思考一下为什么首先在 C 中引入这一点?为什么不将这些操作作为short
?
例如(取自 dyp 在评论中的建议):
short s = 1, t = 2 ;
auto x = s + t ;
x
将具有int类型。
If we look at the Rationale for International Standard—Programming Languages—C in section 6.3.1.8
Usual arithmetic conversions it says (emphasis mine going forward):
The rules in the Standard for these conversions are slight modifications of those in K&R: the modifications accommodate the added types and the value preserving rules. Explicit license was added to perform calculations in a “wider” type than absolutely necessary, since this can sometimes produce smaller and faster code, not to mention the correct answer more often. Calculations can also be performed in a “narrower” type by the as if rule so long as the same end result is obtained. Explicit casting can always be used to obtain a value in a desired type
Section 6.3.1.8 from the draft C99 standard covers the Usual arithmetic conversions which is applied to operands of arithmetic expressions for example section 6.5.6 Additive operators says:
If both operands have arithmetic type, the usual arithmetic conversions are performed on them.
We find similar text in section 6.5.5 Multiplicative operators as well. In the case of a short operand, first the integer promotions are applied from section 6.3.1.1 Boolean, characters, and integers which says:
If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.48) All other types are unchanged by the integer promotions.
The discussion from section 6.3.1.1
of the Rationale or International Standard—Programming Languages—C on integer promotions is actually more interesting, I am going to selectively quote b/c it is too long to fully quote:
Implementations fell into two major camps which may be characterized as unsigned preserving and value preserving.
[...]
The unsigned preserving approach calls for promoting the two smaller unsigned types to unsigned int. This is a simple rule, and yields a type which is independent of execution environment.
The value preserving approach calls for promoting those types to signed int if that type can properly represent all the values of the original type, and otherwise for promoting those types to unsigned int. Thus, if the execution environment represents short as something smaller than int, unsigned short becomes int; otherwise it becomes unsigned int.
This can have some rather unexpected results in some cases as Inconsistent behaviour of implicit conversion between unsigned and bigger signed types demonstrates, there are plenty more examples like that. Although in most cases this results in the operations working as expected.
它不是语言的特性,而是代码运行的物理处理器架构的限制。C 中的int
typer 通常是标准 CPU 寄存器的大小。更多的硅片占用更多空间和更多功率,因此在许多情况下只能对“自然大小”数据类型进行算术运算。这并非普遍正确,但大多数架构仍然存在此限制。换句话说,当添加两个 8 位数字时,处理器中实际发生的是某种类型的 32 位算术,然后是简单的位掩码或其他适当的类型转换。
short
并且char
类型被标准的“存储类型”考虑,即您可以用来节省一些空间但不会为您带来任何速度的子范围,因为它们的大小对于 CPU 来说是“不自然的”。
在某些 CPU 上这是不正确的,但好的编译器足够聪明,可以注意到,如果您例如将一个常量添加到 unsigned char 并将结果存储回 unsigned char 中,则无需进行unsigned char -> int
转换。例如,使用 g++ 为内部循环生成的代码
void incbuf(unsigned char *buf, int size) {
for (int i=0; i<size; i++) {
buf[i] = buf[i] + 1;
}
}
只是
.L3:
addb $1, (%rdi,%rax)
addq $1, %rax
cmpl %eax, %esi
jg .L3
.L1:
您可以在其中看到使用了无符号字符加法指令 ( addb
)。
如果您在短整数之间进行计算并将结果存储在短整数中,也会发生同样的情况。
链接的问题似乎很好地涵盖了它:CPU 只是没有。32 位 CPU 为 32 位寄存器设置了本机算术运算。处理器更喜欢以它最喜欢的大小工作,对于这样的操作,将一个小值复制到本机大小的寄存器是很便宜的。(对于 x86 体系结构,32 位寄存器被命名为好像它们是 16 位寄存器的扩展版本(eax
to ax
、ebx
tobx
等);请参阅x86 整数指令)。
对于一些极其常见的操作,特别是向量/浮点算术,可能有专门的指令对不同的寄存器类型或大小进行操作。对于短的东西,用(最多)16位零填充几乎没有性能成本,并且添加专门的指令可能不值得在模具上花费时间或空间(如果你想真正了解原因;我不确定它们会占用实际空间,但它确实变得更加复杂)。