标准很明确:对小于int
的整数类型执行算术运算时,除非int
不能代表值的全部范围,否则整数首先被提升为有符号的int
。对于原始类型,在这种情况下,促销将改为unsigned int
。
我的问题是:这项政策的动机是什么?为什么将无符号类型提升为有符号的int
,而不是总是提升为unsigned int
?
当然,实际上,几乎没有什么区别,因为底层的汇编指令是相同的(只是零扩展),但是由于溢出,升级到signed int
的关键缺点是没有明显的好处是有符号算术的UB,但在无符号算术中定义明确。
是否有历史原因偏爱签名的int
?是否有不使用二进制补码算法的体系结构,将较小的无符号类型提升为有符号的int
而不是unsigned int
更容易/更快了?
编辑:我认为这很明显,但是我在这里寻找事实(即解释设计决策的一些文档或参考资料),而不是“主要基于观点的”推测。
答案 0 :(得分:10)
此问题在ANSI C Rationale中得到解决(链接指向相关部分3.2.1.1)。在某种程度上,这是一个任意选择,可能会发生任何一种变化,但是有做出选择的理由。
自K&R出版以来,各界之间出现了严重分歧。 积分提升规则发展过程中C的实现。 实施分为两个主要阵营,其特点可能是 为无符号保留和值保留。区别 这些方法之间的重点在于对
unsigned char
的处理 和unsigned short
,当通过整体促销扩大时, 决策也会影响常量的类型(请参阅 §3.1.3.2)。无符号保留方法要求推广两个较小的
unsigned int
的未签名类型。这是一个简单的规则,并得出 与执行环境无关的类型。值保留方法要求将这些类型提升为
signed int
(如果该类型可以正确表示的所有值) 原始类型,否则将这些类型提升为unsigned int
。因此,如果执行环境代表short
如果int
小于unsigned short
,则int
变为unsigned int
; 否则它将变成unsigned int
。
[SNIP]
未签名的保存规则大大增加了
signed int
与{{1}}面对而产生 可疑的签名结果,而价值保留规则 尽量减少这种对抗。因此,价值保留规则是 被认为对新手或程序员而言比较安全。后 经过大量讨论,委员会决定保留价值 规则,尽管UNIX C编译器在 未签名保留的方向。
(我建议阅读全文。我只是不想在这里引用整个内容。)
答案 1 :(得分:0)
凯斯·汤普森(Keith Thompson)的回答中截断了基本原理的有趣部分:
这两种方案在绝大多数情况下都给出相同的答案,并且在具有二进制补码算法和有符号溢出的安静环绕的实现中,甚至在更多情况下,两种方案都给出相同的有效结果-也就是说,在大多数当前实现中。在这样的实现中,只有当这两个条件都成立时,才出现两者之间的差异:
涉及无符号char或unsigned short的表达式会产生一个int范围的结果,在该结果中将设置符号位:即,对这种类型的一元运算,或者对另一个操作数进行二进制运算的二进制运算一个int或``narrower''类型。
前面的表达式的结果用于其符号明显的上下文中:
- sizeof(int)
- 它是右移运算符的左操作数(在实现中 shift定义为算术),或
- 它是/,%,<,<=,>或> =的操作数。
请注意,该标准对实施方式如何处理任何与安静环绕行为相关的情况没有任何要求。明确的含义是,该标准的作者期望在有或没有授权的情况下,针对二进制补码平台的普通实现都可以像上面描述的那样运行,没有迫不得已的理由要做其他事情,因此无需强制要求这样做。 。尽管他们似乎不太可能考虑32位实现给出以下内容的可能性:
unsigned mul(unsigned short x, unsigned short y) { return x*y; }
可能会积极利用不需要容纳大于x
的{{1}}的事实,一些现代平台的编译器将缺乏需求视为邀请来生成将发生故障的代码在这些情况下。