从我从这个问题得到的答案来看,似乎C ++继承了从C执行算术运算时将short转换为int的要求。我是否可以对为什么这首先在C中引入感到由衷?为什么不只将这些操作用作short?例如,(摘录自dyp的建议):

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


x将具有类型的整数。

评论

@Jefffrey积分提升是常规算术转换的一部分。短s = 1,t = 2;自动x = s + t;那么x是一个整数。

maxshort + maxshort> maxshort

@technosaurus不能解释为什么int不提升为long(maxint + maxint> maxint)。

我对这个问题不赞成。这是一个很好的问题,答案很有趣。四票不赞成投票,没有任何意见可言。

@dyp:为什么x是int类型的规则在C和C ++中是完全不同的... ;-)

#1 楼

如果我们在6.3.1.8“通常的算术转换”部分中查看“国际标准-编程语言-C”的原理,则说(强调我的观点):


这些转换的标准规则如下:对K&R中的内容进行了轻微的
修改:修改适应了添加的
类型和值保留规则。显式许可已添加到
,而不是绝对必要的情况下,以“更广泛”的类型执行计算,
因为这有时可以产生更小和更快的代码,所以
不经常提及正确答案。只要获得相同的最终结果,也可以按照规则使用“更窄”的类型执行计算。始终可以使用显式强制转换来获取所需类型的值



C99标准草案的6.3.1.8节涵盖了适用于以下操作数的常用算术转换:例如,第6.5.6节中的算术表达式表示:


如果两个操作数都具有算术类型,则对它们执行常规的算术
转换。


在6.5.5节中也可以找到类似的文字。对于短操作数,首先从6.3.1.1节的布尔值,字符和整数开始应用整数提升,它表示:


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


关于整数促销的基本原理或国际标准-编程语言-C第6.3.1.1节中的讨论实际上更有趣,我将选择性地对b / c进行引用,因为它太长了,无法完全引用:


实施程序分为两个主要阵营,它们可能具有特征性
作为无符号保留和值保留。

[...]

无符号保留方法要求将两个较小的
无符号类型提升为unsigned int。这是一条简单的规则,并产生一个独立于执行环境的
类型。

值保留方法要求将那些类型提升为
signed int,如果该类型可以正确使用的话表示
原始类型的所有值,否则代表将这些类型提升为unsigned
int。因此,如果执行环境将short表示为小于int的某物,则unsigned short将变为int;否则它将成为
unsigned int。


在某些情况下,这可能会产生一些相当意外的结果,因为在unsigned和更大的signed类型之间隐式转换的不一致行为表明,还有更多这样的例子。尽管在大多数情况下,这会使操作按预期进行。

评论


是的,有时它会变得更小,更快,因为您不需要额外的指令来对值进行符号签名/零扩展/扩展为int或屏蔽高位。在x86中,您也不需要额外的指令前缀即可更改参数大小

–phuclv
14年6月24日在2:46

太糟糕了,理由是没有添加一个次要规则,即如果将加法,乘法或按位运算符的结果强制转换为小于int的无符号类型,则表达式的行为就像其操作数同样被强制转换并且对较小的类型。没有确定的情况会与该规则相抵触,但是某些编译器可能会以提升为由来推断x * = y之类的语句。 (两个变量均为无符号短整数)保证x不能超过2147483648 / y。

–超级猫
2015年9月12日18:11



如果我有这样的东西int x = 1234和char * y =&x。 1234的二进制表示形式是00000000 00000000 00000100 11010010。我的机器是小字节序的,因此它将其反转并将其存储在内存中11010010 00000100 00000000 00000000 LSB在前。现在主要部分。如果我使用printf(“%d”,* p)。 printf将读取第一个字节11010010,只有输出是-46,但是11010010是210,所以为什么要打印-46。我真的很困惑,我想一些char到整型促销正在做一些事情,但我不知道。

–苏拉杰(Suraj Jain)
16年8月17日在10:24

您引用了C99标准,但是这种行为是否早于此?我需要上床睡觉,看看我能不能在K&R找到东西。

– PJTraill
17 Mar 9 '17 at 23:39

@PJTraill很好的维基百科指向c89的版本,尽管您无法获得正式草案。在通常的算术转换下的该版本中,它描述了非常相似的过程。所以我会说是的。请注意,上面的引言对K&R中的内容进行了细微修改,因此K&R应该有所不同。

– Shafik Yaghmour
17 Mar 10 '17 at 4:46

#2 楼

它不是语言的功能,而只是对运行代码的物理处理器体系结构的限制。 C语言中的int类型器通常是标准CPU寄存器的大小。更多的硅占用更多的空间和更多的功率,因此在许多情况下,只能对“自然大小”数据类型执行算术运算。并非普遍如此,但是大多数体系结构仍然有此限制。换句话说,当将两个8位数字相加时,处理器中实际发生的是某种类型的32位算术运算,然后是简单的位掩码或另一种适当的类型转换。

评论


我不确定是否一定有遮罩。处理器以其本机字长执行算术运算,然后仅将低位存储回内存。 (而且,虽然您说对了,大多数架构只做单词算术,但英特尔是一个值得注意的例外,它的使用范围非常广泛。)

–詹姆斯·坎泽(James Kanze)
14年6月23日19:00

@JamesKanze你是对的。我按答案编辑。是的,在优化算法方面,尤其是使用itP IP库时,英特尔是领先的。

– Phonon
2014年6月23日19:04

我不同意“这不是语言的功能”;这是语言的功能。之所以这样定义,是因为...但它是由语言而不是处理器定义的。

–乔纳森·莱弗勒(Jonathan Leffler)
2014年6月23日23:55在

@JonathanLeffler这肯定是语言的功能。我认为在大多数语言中。但是声子的答案解释了为什么语言具有此功能。 (可能值得指出的是,在过去,机器只有字,没有字节,半字等。当引入字节寻址时,它只影响内存访问,而不影响寄存器和操作。因此,PDP-11拥有字节和字指令,当字节指令的目标地址是寄存器时,该字节被符号扩展到一个字。)

–詹姆斯·坎泽(James Kanze)
14年6月24日在7:58

用户代码完全隐藏了CPU执行命令的方式。您根本没有回答问题。

–苏菲特
14年6月24日在20:37

#3 楼

shortchar类型是“存储类型”的标准类型,即可以用于节省空间但不会为您带来任何速度的子范围,因为它们的大小对于CPU而言是“不自然的”。 />
在某些CPU上并非如此,但是好的编译器足够聪明,可以注意到如果您将常量添加到无符号字符并将结果存储回无符号字符,则无需进行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    , (%rdi,%rax)
    addq    , %rax
    cmpl    %eax, %esi
    jg  .L3
.L1:


,在这里您可以看到使用了无符号字符加法指令(addb)。

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

#4 楼

链接的问题似乎很好地涵盖了这一点:CPU却没有。 32位CPU为32位寄存器设置了其本机算术运算。处理器更喜欢以自己喜欢的大小工作,对于这样的操作,将较小的值复制到本机大小的寄存器中很便宜。 (对于x86体系结构,将32位寄存器命名为好像它们是16位寄存器的扩展版本(eaxaxebxbx等);请参见x86整数指令)。

对于某些极其常见的操作,尤其是矢量/浮点运算,可能会有专门的指令对不同的寄存器类型或大小进行操作。对于较短的内容,(最多)16位零填充几乎没有性能成本,添加专用指令可能不值得在芯片上花费时间或空间(如果您想真正了解原因,我是不确定他们会占用实际空间,但是确实会变得更复杂。)

评论


这不仅仅是一个硬件问题,在起草C99标准的过程中做出了明智的选择,以使整数促销以特定方式工作。

– Shafik Yaghmour
14年6月24日在2:24

“请注意,也对32位寄存器进行了命名,就像它们是16位寄存器的扩展版本一样(从eax到ax,从ebx到bx等)”,对于x86是正确的,但对于大多数其他体系结构却不正确。无论在32位或64位模式下,MIPS寄存器都具有相同的名称,并且它们始终以本机大小工作,因此无论如何您都无法以8位或16位进行算术运算

–phuclv
2014年6月24日在2:41