连续应用两个显式类型转换的原因是什么?

Pio*_*ost 2 c++ casting

应用两个显式类型转换的原因是什么?

if (unlikely(val != (long)(char)val)) {
Run Code Online (Sandbox Code Playgroud)

代码取自lxml源代码包中的lxml.etree.c源文件.

pli*_*nth 10

这是一种廉价的方法来检查高位中是否有任何垃圾.上面的8位,24位或56位(取决于sizeof(val))的char铸造印章然后将其提升回来.如果char已签名,它也将签名扩展.

更好的测试可能是:

if (unlikely(val & ~0xff)) {
Run Code Online (Sandbox Code Playgroud)

要么

if (unlikely(val & ~0x7f)) {
Run Code Online (Sandbox Code Playgroud)

取决于此测试是否关心第7位.

只是为了笑容和完整性,我写了以下测试代码:

void RegularTest(long val)
{
    if (val != ((int)(char)val)) {
        printf("Regular = not equal.");
    }
    else {
        printf("Regular = equal.");
    }
}

void MaskTest(long val)
{
    if (val & ~0xff) {
        printf("Mask = not equal.");
    }
    else {
        printf("Mask = equal.");
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是在Visual Studio 2010中调试中的演员代码:

movsx   eax, BYTE PTR _val$[ebp]
cmp DWORD PTR _val$[ebp], eax
je  SHORT $LN2@RegularTes
Run Code Online (Sandbox Code Playgroud)

这是掩码:

mov eax, DWORD PTR _val$[ebp]
and eax, -256               ; ffffff00H
je  SHORT $LN2@MaskTest
Run Code Online (Sandbox Code Playgroud)

在发布中,我得到了这个演员代码:

movsx   ecx, al
cmp eax, ecx
je  SHORT $LN2@RegularTes
Run Code Online (Sandbox Code Playgroud)

在发布中,我得到掩码代码:

test    DWORD PTR _val$[ebp], -256      ; ffffff00H
je  SHORT $LN2@MaskTest
Run Code Online (Sandbox Code Playgroud)

发生什么了?在转换的情况下,它正在使用符号扩展执行一个字节mov(ha!bug - 代码不一样,因为字符被签名)然后比较并且完全偷偷摸摸,编译器/链接器也使这个函数使用寄存器传递为了论证.在发布中的掩码代码中,它将所有内容都折叠成一条测试指令.

哪个更快?打败我 - 坦率地说,除非你在非常慢的CPU上运行这种测试或运行数十亿次,否则无关紧要.至少没有.

因此,在这种情况下的答案是编写清楚其意图的代码.我希望C/C++骑师可以看一下掩码并了解它的意图,但是如果你不喜欢它,你应该选择这样的东西:

#define BitsAbove8AreSet(x) ((x) & ~0xff)
#define BitsAbove7AreSet(x) ((x) & ~0x7f)
Run Code Online (Sandbox Code Playgroud)

要么:

inline bool BitsAbove8AreSet(long t){return(t&~0xff)!= 0; } //使它成为一个好的内联bool BitsAbove7AreSet(long t){return(t&~0x7f)!= 0; }

并使用谓词而不是实际代码.

总的来说,我认为"它便宜吗?" 除非你在一些非常具体的问题领域工作,否则这个问题并不是一个特别好的问题.例如,我从事图像处理工作,当我从一个图像到另一个图像进行某种操作时,我经常会看到如下代码:

BYTE *srcPixel = PixelOffset(src, x, y, srcrowstride, srcdepth);
int srcAdvance = PixelAdvance(srcrowstride, right, srcdepth);
BYTE *dstPixel = PixelOffset(dst, x, y, dstrowstride, dstdepth);
int dstAdvance = PixelAdvance(dstrowstride, right, dstdepth);
for (y = top; y < bottom; y++) {
    for (x=left; x < right; x++) {
        ProcessOnePixel(srcPixel, srcdepth, dstPixel, dstdepth);
        srcPixel += srcdepth;
        dstPixel += dstdepth;
    }
    srcPixel += srcAdvance;
    dstPixel += dstAdvance;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,假设ProcessOnePixel()实际上是一块内联代码,将被执行数十亿次数十亿次.在这种情况下,我非常关心不做函数调用,不做冗余工作,不重新检查值,确保计算流程将转化为明智地使用寄存器的东西,等等.但我实际主要关心的是代码可以被下一个看着它的可怜的骗子(可能是我)读.

在我们当前的编码世界中,对于几乎每个问题领域来说,最好花费一点时间在前面确保您的代码易于阅读和维护,而不是担心性能超出门.

  • 真的很便宜吗?我的意思是,它比bit-masking更具可读性,我希望编译器能够生成相同的(最好的)或更糟的代码(更糟糕的是),但我看不出编译器如何推断这个奇怪的转换是否打算是一个掩盖和相应优化. (4认同)