【发布时间】:2015-06-21 17:38:14
【问题描述】:
在标准化之前的早期 C 语言中,实现有多种方法来处理各种动作的异常和半异常情况。其中一些会触发陷阱,如果没有先配置,可能会导致随机代码执行。因为此类陷阱的行为超出了 C 标准的范围(并且在某些情况下可能由运行程序控制之外的操作系统控制),并且避免要求编译器不允许依赖于此类的代码陷阱要继续这样做,可能导致此类陷阱的操作行为完全由编译器/平台自行决定。
到 1990 年代末,尽管 C 标准没有要求这样做,但每个主流编译器都在其中许多情况下采用了通用行为;使用此类行为可以提高代码速度、大小和可读性。
既然不再支持请求以下操作的“明显”方式,那么在使用旧编译器时,应该如何以不妨碍可读性或对代码生成产生不利影响的方式替换它们?为了便于说明,假设int 是32 位,ui 是无符号整数,si 是有符号整数,b 是无符号字符。
给定
ui和b,计算 b==0..31 时的ui << b,或任意表现为ui << (b & 31)的值或值 32..255 时为零的值。请注意,如果当右侧操作数超过 31 时左侧操作数为零,则两种行为将相同。对于只需要在处理器上运行的代码,当右移或左移从 32 到 255 时产生零,计算
ui << b为 b==0..31 和 0 为b==32..255。虽然编译器可能能够优化为跳过值 32..255 的移位而设计的条件逻辑(因此代码将简单地执行将产生正确行为的移位),但我不知道有什么方法可以制定这种条件逻辑这将保证编译器不会为它生成不必要的代码。与 1 和 2 一样,但用于右移。
-
给定
si和b使得 b0..30 和si*(1<<b)不会溢出,计算si*(1<<b)。请注意,使用乘法运算符会严重影响许多旧编译器的性能,但如果移位的目的是缩放有符号值,则在操作数在整个移位过程中保持负数的情况下强制转换为无符号感觉是错误的。 -
给定各种整数值,执行加法、减法、乘法和移位,如果没有溢出,结果将是正确的,如果有溢出,代码将产生高位行为的值以非陷阱和非 UB,但以其他方式不确定或将以平台定义的方式可识别陷阱(并且在不支持陷阱的平台上,只会产生不确定的值)。
给定一个指向分配区域的指针和一些指向其中事物的指针,使用
realloc更改分配大小并调整上述指针以匹配,同时避免在realloc返回原始块。不一定在所有平台上都可能,但 1990 年代的主流平台都允许代码确定realloc是否导致事物移动,并通过减去该指针的前基地址来确定指针到死对象的偏移量曾经是多少对象(请注意,需要通过计算与每个死指针相关联的偏移量,然后将其添加到新指针来完成调整,而不是通过尝试计算旧指针和新指针之间的“差异”——这将是合法的在许多分段架构上失败)。
“超现代”编译器是否提供了对上述内容的任何良好替代品,不会降低代码大小、速度或可读性中的至少一个,而对其他任何一个都没有改进?据我所知,不仅 1990 年代 99% 的编译器可以完成上述所有操作,而且对于每个示例,几乎所有示例都可以以相同的方式编写代码。一些编译器可能已经尝试使用无保护的跳转表来优化左移和右移,但这是我能想到的唯一情况,1990 年代平台的 1990 年代编译器会对“明显”的编码方式有任何问题以上任何一项。如果超现代编译器不再支持经典形式,它们提供什么替代品?
【问题讨论】:
-
我认为替换是如果你想要一个汇编器,使用一个。长大了的 C 不想再假装自己了。
-
谷歌搜索“每个程序员都应该知道的关于未定义行为的知识”,并找到 Chris Lattner 的博客文章(Chris Lattner = LLVM 负责人和 Swift 的发明者)。
-
@WumpusQ.Wumbley:如果 C 不应该是汇编语言的升级版,这将允许代码在不同平台之间移植,这取决于它们之间的相似性,它是什么?为什么需要 C 功能并受益于其改进而不是汇编语言的程序员应该放弃这些改进?
-
我可以看到你已经建立了很多咆哮,不想在它完成之前停止它。但它会随着你学习写
b>=0&&b<32?ui<<b:0而结束,这就是你的意思,并接受花在它上面的额外周期并不重要(因为你现在的周期比你在 1990 年代平台上的周期多得多),并且期望平台之间的可移植性与“移位操作符应该总是像我喜欢的 CPU 上的操作符一样”共存是不明智的 -
@WumpusQ.Wumbley:我编写了很多嵌入式系统代码,其中像这样的额外周期通常很重要,而且 1990 年代的 C 比汇编更好。如果将一个函数提供给旧的编译器需要 30K 的工作代码,那么将相同的函数提供给一个新的编译器将产生 20K 的不工作的代码,并且给新编译器提供一个经过修改的版本可以使用它产生 31K 的运行速度比原来慢的代码,我看不到新编译器的好处。上面的场景不现实吗?
标签: c language-lawyer undefined-behavior