LWN:char 是否应该是signed?
关注了就能看到更多这么棒的文章哦~
Would you like signs with those chars?
By Jonathan Corbet
October 24, 2022
DeepL assisted translation
https://lwn.net/Articles/911914/
C 语言之所以让人沉迷的众多原因(怪癖)之一,就是那些它未定义的行为。其中就包括 char 变量是否是有符号的数值(signed quantity)。是或者不是,通常都不会产生什么影响,但也有例外情况。内核代码在许多不同的架构上运行,经常就能找到这些例外情况。最近的一个讨论就是希望能消除 char 变量是否有符号的这个不确定性,但没能更进一步,至少没有朝着它最初尝试的方向。
一般来说,C的整数类型(integer types)是有符号的,除非另有规定;short、int、long 都是这样的。但是 char 在目前的机器上通常是一个单字节变量,它就不太一样;它可以是有符号的,也可以不是,取决于在相应的体系架构上最方便的实现方式。在 x86 系统上,char 变量是有符号的,除非明确声明为 unsigned char。然而在 Arm 系统上,char 变量就是 unsigned(除非明确声明为 signed)。
对于开发者来说,char 变量可能是有符号的,也可能没有,这不是一件很容易记住的事情,特别是当开发者的工作通常都集中在一个特定的架构上的时候。因此,x86 系统的开发者可能会养成习惯,认为 char 总是 signed,结果,写出的代码在其他系统上运行时行为就不对了。Jason Donenfeld 最近遇到了这种错误,在 fix 了之后,他又发布了一个 patch,希望能在内核中整体地解决这个问题。为了试图 "完全消除这种特殊的不一致的 signed 相关的 bug",它在编译器命令行中加入了 -fsigned-char 这个 flag,从而使得直接定义的 char 类型在所有架构上都是 signed 类型的。
这一改动并不受欢迎。Segher Boessenkool 指出,这导致 ABI 发生了变化,并可能损害那些天生希望 char 是 unsigned 的系统中的性能。Linus Torvalds 同意这个观点,他说。"我们应该对这些标准中措辞表示接纳,并意识到 'char' 在这方面是具有不确定性的"。然而,他不同意 Boessenkool 的建议,也就是删除现在在使用的 -Wno-pointer-sign 选项(从而启用-Wpointer-sign 警告)。这个改动将使 signed 和 unsigned char 类型的指针混合使用时报出 warning;Torvalds 抱怨说,当使用 char 变量时,这个机制并不会给出 warning,相反在正确的代码中却产生了大量的 false positive 的 warning。
不过,在讨论的后期,Torvalds 想知道是否真的有必要确定 char 变量是不是 signed,但是反过来,强制它们默认是 unsigned,而不是 signed。他说,这在所有常见的体系架构上都不会让生成的代码变坏。"我确实认为,在各个架构上如果有一些奇怪差异,就通常是个坏主意,能让 C 语言的规则更严格从而避免这些差异是件好事"。有趣的是,他指出,有了这个选项之后,像这样的代码:
仍然会在 -Wpointer-sign 选项下产生一个 warning,因为 string constant pointer 这种字符串常量指针仍然被认为是一个没有指明 signed 与否的 char * 类型,那么对它的处理就跟明确定义了的 unsigned char * 类型是不一样的。"你 真的 无法完全搞定这个情况。这就是个别人控制了的游戏,就像一些怪胎的狂欢游戏"。
Donenfeld 在这个想法中还是看到了一些好处,尽管他认为这确实会破坏一些代码。他给出了一个新的 patch,在编译器命令行中加入了 -funsigned-char,从而实现这个改动。他曾建议,鉴于在 6.1 版本发布前都有时间 fix 它所带来的全部后果,因此也许可以马上先合入掉,但 Torvalds 拒绝了这个想法。"如果我们还在 merge 窗口期间的话,我可能会真的合入,但就目前的情况来看,我认为它应该进入 linux-next,呆在那里为下一个合并窗口做准备。他补充说,这种改动所导致的任何问题都可能是一些很细微的问题,而且是在那些不需要支持各种架构的驱动代码中。核心内核代码其实就不一样,它一直需要能支持跨体系架构都可以很好工作,所以他认为这里不会有问题。
所以 Donenfeld 的 patch 被放在了 linux-next 中,等待 12 月的 6.2 合并窗口。从而社区可以在 2 月底之前来发现那些因为把 char 变量在 Linux 支持的所有架构上都假设成 unsigned 而可能引出的问题。这是一个相当长的时间,但真正开始在尽可能多的环境中测试这一改动也肯定不会太早。毕竟,这是对编写内核的 C 语言的一个根本性的改变;如果没有产生意外,本身就是一个意外了。
要想识别出潜在问题,有一个方法是找到当 char 被强制为 unsigned 时生成的代码会发生变动的地方。Torvalds 已经在这个方向上做了一些努力,Kees Cook 使用了一个用来检查 reproducible builds 而设计的系统,发现了大量的变动。其中许多变动应该都会被证明是无害的,但确定的唯一方法就是真正看一下这些代码。同时,Alexey Dobriyan 发布的一个 fix 引出了 Torvalds 的一个额外要求,就是希望将 char 相关的 fix 都收集到同一个代码 tree 里。随着这些 fix 积累起来,最终结果应该会给人们一个整体印象,来说明这一改动实际上会造成多大的破坏。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~