【问题标题】:Is plain char usually/always unsigned on non-twos-complement systems?在非二进制补码系统上,普通字符通常/总是无符号吗?
【发布时间】:2011-09-04 03:23:06
【问题描述】:

显然,标准对此只字未提,但从实际/历史的角度来看,我更感兴趣:具有非二进制补码算法的系统是否使用无符号的普通 char 类型?否则,您可能会遇到各种奇怪的情况,例如空终止符的两种表示形式,以及无法表示 char 中的所有“字节”值。这种奇怪的系统真的存在吗?

【问题讨论】:

  • 请注意,当您认为strcmp 需要将字节与unsigned char 进行比较时,怪异会变得更加严重,但可能必须在达到(任一)空终止符字节时停止字符串。
  • “这种奇怪的系统真的存在吗?”我认为不存在。

标签: c twos-complement unsigned-char


【解决方案1】:

用于终止字符串的空字符永远不能有两种表示形式。它是这样定义的(即使在 C90 中):

一个所有位都设置为0的字节,称为空字符,应该存在于基本执行字符集中

因此,补码上的“负零”是行不通的。

也就是说,我真的对非二进制补码 C 实现知之甚少。我在大学时使用过一个补码机器,但对它的记忆不多(即使我当时关心标准,它还是在它存在之前)。

【讨论】:

  • 我同意它不会由字符串文字或字符串函数产生,但看起来如果通过算术运算你最终得到一个负零并将其分配到一个试图终止字符串的 char 数组中,你可能认为你终止了它,但实际上失败了......因此,任何这样行为的代码都可能存在轻微的可移植性缺陷......
【解决方案2】:

确实,在商业生产的计算机的前 10 年或 20 年(1950 年代和 60 年代),对于如何用二进制表示负数,显然存在一些分歧。实际上有 三个 竞争者:

  1. 二人互补,不仅打赢了战争,还把其他人灭了
  2. 补码,-x == ~x
  3. 符号幅度,-x = x ^ 0x80000000

我认为最后一个重要的补码机器可能是 CDC-6600,它在当时是地球上最快的机器,也是第一台超级计算机的前身。1.

很遗憾,您的问题无法真正得到回答,不是因为这里没有人知道答案 :-) 而是因为永远不必做出选择。这实际上是出于两个原因:

  1. 二进制补码与字节机器同时接管。字节寻址通过二进制补码 IBM System/360 风靡全球。以前的机器没有字节,只有完整的 words 有地址。有时程序员会在这些单词中包含字符,有时他们会使用整个单词。 (字长从 12 位到 60 位不等。)

  2. 直到字节机和二进制补码转换十年后才发明了 C。第 1 项发生在 1960 年代,C 在 1970 年代首次出现在小型机器上,直到 1980 年代才接管世界。

因此,机器从未有过有符号字节、C 编译器和二进制补码数据格式以外的其他东西。空终止字符串的想法可能是一个汇编语言程序员一个又一个反复发明的设计模式,但我不知道它是由编译器指定的,直到 C 时代。

在任何情况下,第一个实际标准化的 C("C89") 只是简单地指定 “附加零值的字节或代码”,从他们试图独立于数字格式的上下文。因此,“+0”是理论上的答案,但实际上可能永远不会真正存在。


1. 6600 是历史上最重要的机器之一,不仅仅是因为它速度快。它由 Seymour Cray 自己设计,引入了乱序执行和各种其他元素,后来统称为“RISC”。尽管其他人试图声称功劳,但 Seymour Cray 是 RISC 架构的真正发明者。毫无疑问,他发明了超级计算机。实际上很难说出他没有设计的过去的“超级计算机”。

【讨论】:

  • 真的没有带有补码或符号量的实际 C 实现吗?如果没有现有的实现,为什么 C 标准会费心允许它们,因为它们显然是一个需要关心的主要问题。
  • @R,没有实施者会关心,当然,由于麻烦,但标准委员会通常不是实施者。他们会将自己的任务视为定义一种通用语言。毕竟,C 是“可移植的汇编语言”,而且我敢肯定,他们在大型机制造商处设想了崩溃程序来实现 C。真正的原因可能是:它在1989 年,所有这些价值百万美元的机器变得多么快,不仅是旧的,而且是“你必须付钱才能拖走的废品”。 原来, 之间只有一线之隔“昂贵的充满房间的电脑”“有毒废物”。
  • 我认为他们已经遇到了-0 != 0 的问题。据我了解,您可以执行诸如添加0 + -0 之类的操作并得到一个真正的零,因此在比较之前这样做很常见。我怀疑情况会有点像今天有签名和未签名的thpes,-0 绝对不会是字符串终止符。
  • @R.. - 直到 21 世纪,Univac/Unisys 生产了一个补充硬件。如果 C 语言委员会因为不兼容的字符串终止符而无法在他们的机器上实现,那就太糟糕了!
  • @Bo:这绝不是不可能的。即使硬件旨在用于补码,您也可以始终使用二进制补码,只需在生成机器代码时忽略带符号的指令并始终生成无符号指令。乘法和除法需要一些小的修补,但基本算术没问题。
【解决方案3】:

我相信系统几乎但不太可能有一个补码 'char' 类型,但有四个问题不能全部解决:

  1. 每个数据类型都必须可以表示为一个 char 序列,这样如果包含两个对象的所有 char 值比较相同,则包含的数据对象将是相同的。
  2. 同样,每种数据类型都必须可以表示为“无符号字符”序列。
  3. 可以将任何数据类型分解成的 unsigned char 值必须形成一个组,其顺序是 2 的幂。
  4. 我不相信标准允许一个补码机器对负零值进行特殊处理并使其表现得像其他东西。

如果获得负零的唯一方法是覆盖一些其他数据类型,并且如果比较负零,则可能有一台符合标准的机器具有一个补码或符号幅度“char”类型不等于正零。我不确定这是否符合标准。

编辑

顺便说一句,如果放宽要求 #2,我想知道将其他数据类型覆盖到“char”上时的确切要求是什么?除其他事项外,虽然该标准非常清楚地表明,必须能够对可能由于将另一个变量覆盖到“char”上而产生的任何“char”值执行赋值和比较,但我不知道它是否有任何要求所有这些值都必须表现为算术组。例如,我想知道一台机器的合法性是什么,其中每个内存位置物理存储为 66 位,前两位表示该值是否是 64 位整数、32 位内存句柄加上 32位偏移量,还是 64 位双精度浮点数?由于标准允许实现在算术计算超出有符号类型的范围时做任何他们喜欢的事情,这表明有符号类型不一定必须表现为一个组。

对于大多数有符号类型,不要求该类型不能表示超出limits.h 中指定范围的任何数字;如果limits.h 指定最小“int”是-32767,那么实现实际上允许-32768 的值是完全合法的,因为任何尝试这样做的程序都会调用未定义的行为。关键问题可能是,由某些其他类型的覆盖产生的“char”值是否合法,以产生超出limits.h中指定范围的值。我想知道标准是怎么说的?

【讨论】:

  • 从哪里得到 1 和 2?我只知道 2。
  • @R.. - 这不是等价物。如果 char 表示相同,则值相同。但是如果有填充位,即使字符表示不同,它们也可能相等。
  • 字符类型,至少unsigned char,根据定义不能有填充位。
  • @R.:我承认我不知道标准是否实际指定 (2),但我已经看到了足够多的隐式依赖它的实现(例如返回的内存分配函数'unsigned char') 我推断这是真的。危险我承认。我会在我的答案中添加一个附录。顺便说一句,对于填充位,当且仅当符合标准的程序无法知道它们的存在时,才允许使用它们。例如,当 C 代码在原始 IBM PC 或 AT 上运行时,每个字节在硬件中都有一个额外的奇偶校验位;可以用机器码做一些技巧......
  • @R:...故意错误设置某些字节的奇偶校验数据(并使用读取此类字节时会发生的 NMI 作为捕获对未初始化数据的读取的一种手段)但是有符合标准的程序无法显式控制这些位或检测它们的存在。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-09
  • 2016-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-10
  • 1970-01-01
相关资源
最近更新 更多