为什么在C和c++中进行算术运算之前必须将short类型转换为int类型?



从我从这个问题得到的答案来看,c++在从C执行算术运算时继承了将short转换为int的要求。我可以向您请教一下为什么这首先是在C中引入的吗?为什么不直接用short来做这些操作呢?

例如(取自dyp在评论中的建议):

short s = 1, t = 2 ;
auto  x = s + t ;

x的类型为int

如果我们看国际标准编程语言- c的基本原理6.3.1.8 通常的算术转换它说(强调我的未来):

标准中对于这些转换的规则是轻微的K&R中的修改:修改适应添加的内容类型和值保持规则。添加显式许可在非绝对必要的情况下,以"更宽"类型执行计算;因为这有时会产生更小更快的代码,所以不要这样做更频繁地提到正确答案。计算也可以按照as if规则以"较窄"的类型进行,只要相同得到最终结果。显式强制转换总是可以用来获得所需类型的值

C99标准草案第6.3.1.8节涵盖了通常的算术转换,它应用于算术表达式的操作数,例如第6.5.6节加性运算符说:

如果两个操作数都是算术类型,则通常是算术类型是对它们执行转换。

在6.5.5节中也可以找到类似的文本。在操作数的情况下,首先从6.3.1.1节中应用整数提升,即

如果int型可以表示原类型的所有值,则该值为转换为int型;否则,将其转换为unsigned int类型。这些被称为整数提升48)所有其他类型都是

国际标准编程语言- c 关于整数提升6.3.1.1节的讨论实际上更有趣,我将有选择地引用b/c,因为太长而不能完全引用:

的实现可以分为两大阵营as unsigned preserving and value preserving

[…]unsigned保持方法要求提升两个较小的Unsigned类型转换为Unsigned int。这是一个简单的规则,结果是a与执行环境无关的类型。

保值方法要求将这些类型提升为类型的所有值,则为Signed int原始类型,否则将这些类型提升为无符号类型int。因此,如果执行环境将short表示为小于int, unsigned short变为int;否则它就变成了unsigned int。

在某些情况下,这可能会导致一些意想不到的结果,如unsigned类型和大signed类型之间隐式转换的不一致行为所示,还有更多这样的例子。尽管在大多数情况下,这会导致操作按预期工作。

与其说这是语言的特性,不如说是运行代码的物理处理器体系结构的限制。C语言中的int类型通常是标准CPU寄存器的大小。更多的芯片占用更多的空间和更多的功率,因此在许多情况下,算术只能在"自然大小"的数据类型上进行。这并不是普遍正确的,但是大多数体系结构仍然有这个限制。换句话说,当两个8位数字相加时,处理器中实际发生的是某种32位算术,后面跟着一个简单的位掩码或另一个适当的类型转换。

shortchar类型被认为是标准的"存储类型",即子范围,您可以使用它们来节省一些空间,但不会为您带来任何速度,因为它们的大小对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:

在这里你可以看到一个unsigned char addition指令(addb)被使用。

如果在短整型之间进行计算并将结果存储在短整型中,也会发生同样的情况。

链接的问题似乎很好地涵盖了它:CPU只是没有。32位CPU为32位寄存器设置了其本机算术运算。处理器倾向于以自己喜欢的大小工作,对于这样的操作,将一个小值复制到本机大小的寄存器中是很便宜的。(对于x86架构,32位寄存器被命名为16位寄存器的扩展版本(eaxax, ebxbx,等等);

对于一些非常常见的运算,特别是向量/浮点运算,可能会有专门的指令在不同的寄存器类型或大小上操作。对于像short这样的内容,使用(最多)16位零填充的性能成本非常小,并且添加专门的指令可能不值得在骰子上花费时间或空间(如果你想真正了解为什么;我不确定它们是否会占用实际的空间,但它确实会变得更复杂)。

最新更新