在标准化之前的早期C中,实现有多种方法来处理各种操作的异常和半例外情况。其中一些会触发陷阱,如果不首先配置,这些陷阱可能会导致随机代码执行。由于这类陷阱的行为超出了C标准的范围(在某些情况下可能由运行程序控制之外的操作系统控制),并且为了避免要求编译器不允许依赖这种陷阱的代码继续这样做,可能导致这种陷阱的行为完全由编译器/平台自行决定。
到20世纪90年代末,虽然C标准并不要求这样做,但每个主流编译器都对其中的许多情况采用了常见的行为;使用这些行为将允许在代码速度、大小和可读性方面进行改进。
既然不再支持请求以下操作的“明显”方法,那么在使用旧编译器时,应该如何替换它们,使其不妨碍可读性,也不会对代码生成产生不利影响?为了便于描述,假设int为32位,ui为无符号int,si为有符号int,b为无符号字符。
ui和b,计算b==0..31的ui << b,或者对于32.255值可以任意表现为ui << (b & 31)或0的值。注意,当右操作数超过31时,如果左手操作数为零,则这两种行为将是相同的。ui << b表示b==0..31,计算0表示b==32..255。虽然编译器可能能够优化设计为跳过值32.255的移位的条件逻辑(因此代码只需执行转换,从而产生正确的行为),但我不知道如何制定这样的条件逻辑,以保证编译器不会为其生成不必要的代码。si和b使得b0..30和si*(1<<b)不会溢出,则计算si*(1<<b)。请注意,使用乘法运算符会严重损害许多旧编译器的性能,但如果转换的目的是缩放有符号值,则在操作数在整个移位过程中保持负值的情况下,转换为无符号值是错误的。realloc更改分配大小并调整上述指针以匹配,同时避免在realloc返回原始块的情况下进行额外工作。在所有平台上都不一定可能,但90年代主流平台都允许代码确定realloc是否导致事物移动,并通过减去该对象的前基址来确定指针指向死对象的偏移量(注意,调整需要通过计算每个死指针的偏移量来完成,然后将其添加到新指针,而不是试图计算旧指针和新指针之间的“差异”--这在许多分段体系结构上是合理的)。“超现代”编译器是否为上面的代码提供了任何好的替代品,它们不会降低至少一个代码的大小、速度或可读性,而不提供任何其他方面的改进?据我所知,在整个1990年代,不仅99%的编译器能够完成上述所有的工作,而且在每个例子中,几乎所有的编译器都能以相同的方式编写代码。一些编译器可能试图用一个没有防护的跳转表来优化左移和右移,但这是我唯一能想到的情况,上世纪90年代为一个平台编写的编译器对上述任何一种“明显”的编码方式都会有任何问题。如果那个超现代的编译器已经不再支持经典的形式,他们作为替代者会提供什么呢?
发布于 2015-05-13 20:52:24
现代标准C的指定方式可以保证它是可移植的,前提是您编写代码时不需要比标准C抽象机器给出的底层硬件更多的期望。
对于给定的目标CPU和体系结构,仍然可以为在给定的优化级别上具有特定行为的特定编译器编写,但不要期望任何其他编译器(现代的或其他的,甚至是您所编写的编译器的一个小的修订版),在您的代码违反以下条件时,尝试直观您的期望:标准说,期望任何定义良好的实现不可知论行为是不合理的。
发布于 2016-01-16 05:15:01
两个一般原则适用于标准C和标准C++:
gcc -S),如果发现它未能优化定义良好的行为以使机器指令合适,则将向编译器的发行者提交一个缺陷报告。(但是,当针对特定平台的唯一实用编译器的发行者对优化不太感兴趣时(例如针对MOS 6502的cc65 ),这是行不通的。根据这些原则,您通常可以导出一种定义良好的方法来实现相同的结果,然后将信任-但-验证原则应用于生成代码的质量。例如,使转换函数具有定义良好的行为,并让优化器删除架构本身所保证的任何不必要的检查。
// Performs 2 for unsigned numbers. Also works for signed
// numbers due to rule for casting between signed and unsigned
// integer types.
inline uint32_t lsl32(uint32_t ui, unsigned int b) {
if (b >= 32) return 0;
return ui << b;
}
// Performs 3 for unsigned numbers.
inline uint32_t lsr32(uint32_t ui, unsigned int b) {
if (b >= 32) return 0;
return ui >> b;
}
// Performs 3 for signed numbers.
inline int32_t asr32(int32_t si, unsigned int b) {
if (si >= 0) return lsr32(si, b);
if (b >= 31) return -1;
return ~(~(uint32)si >> b);
}对于4和5,转换为未签名,做数学,并返回到签名。这会产生非诱捕性的、定义明确的行为。
https://stackoverflow.com/questions/29655664
复制相似问题