【问题标题】:Why do unsigned "small" integers promote to signed int?为什么无符号“小”整数提升为有符号整数?
【发布时间】:2022-01-20 12:36:12
【问题描述】:

标准很明确:当对小于int的整数类型执行算术运算时,整数首先被提升为有符号int,除非int不能代表原始类型的全部值范围,在在这种情况下,促销将改为unsigned int

我的问题是:这个政策的动机是什么?为什么将无符号类型提升为有符号 int,而不是始终提升为 unsigned int

当然,实际上几乎没有区别,因为底层汇编指令是相同的(只是零扩展名),但是升级到 signed int 有一个关键的缺点,没有明显的优点,因为溢出是 UB在有符号算术中,但在无符号算术中定义良好。

选择签名int 是否有历史原因?是否存在不使用二进制补码算法的架构,其中将小型无符号类型提升为有符号 int 而不是 unsigned int 更容易/更快?

编辑:我认为这很明显,但我在这里寻找事实(即解释设计决策的一些文档或参考资料),而不是“主要基于意见”的推测。

【问题讨论】:

  • 减法可能会产生有符号值? “基本” int 类型已签名?
  • 我一直觉得 C 的有符号与无符号规则有点奇怪。例如,在比较有符号整数和无符号整数时,它会执行无符号比较,这对我来说似乎是错误的。所以-1 < (unsigned) 0 产生0。当然,不管你走哪条路,总会有范围的问题,除非你推广到更大的尺寸。但是,是的,在增加大小时对无符号数量进行签名似乎很奇怪。
  • 最初没有指定(K&R 1.st edition)。一些编译器采用了一种方式,错误的行为被标准化了,因为人们显然没有意识到“保值”促销是可移植性的噩梦,而不是理智的“未签名”促销。

标签: c standards


【解决方案1】:

这在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 面对 signed int 以产生一个 有问题的签名结果,而保值规则 尽量减少这样的对抗。因此,保值规则是 被认为对新手或粗心的程序员更安全。后 经过多次讨论,委员会决定支持保值 规则,尽管 UNIX C 编译器已经在 无符号保存的方向。

(我建议阅读完整部分。我只是不想在这里引用整个内容。)

【讨论】:

【解决方案2】:

基本原理的一个有趣部分摘自 Keith Thompson 的回答:

这两种方案在绝大多数情况下都给出了相同的答案,并且在使用二进制补码算术和无符号溢出的安静环绕的实现中甚至在更多情况下都给出了相同的有效结果——也就是说,在大多数当前的实现中.在这样的实现中,只有当这两个条件都为真时,两者之间的差异才会出现:

  1. 涉及 unsigned char 或 unsigned short 的表达式会产生一个 int 范围的结果,其中设置了符号位:即,对这种类型的一元运算,或者另一个操作数为的二元运算一个 int 或“窄”类型。

  2. 前面表达式的结果在其符号性很重要的上下文中使用:

    • sizeof(int)
    • 它是右移运算符的左操作数(在实现中,此 shift 被定义为算术),或
    • 它是 /、%、 或 >= 的操作数。

请注意,该标准对实现如何处理与安静环绕行为相关的任何情况没有任何要求。明确的含义是,标准的作者期望二进制补码平台的常见实现在有或没有授权的情况下都会像上面描述的那样运行,没有令人信服的理由不这样做,因此没有必要强制他们这样做.虽然他们似乎不太可能考虑 32 位实现的可能性,例如:

unsigned mul(unsigned short x, unsigned short y) { return x*y; }

可能会积极利用它不需要容纳大于 2147483647/yx 的值这一事实,一些现代平台的编译器将缺乏要求视为生成在这些情况下会发生故障的代码的邀请。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-10
    • 2017-07-01
    • 2014-10-27
    • 2019-12-21
    • 2016-02-17
    • 2012-01-09
    相关资源
    最近更新 更多