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

时间:2011-02-25 15:20:58

标签: c++ casting

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

if (unlikely(val != (long)(char)val)) {

从lxml的源包中的lxml.etree.c源文件中获取的代码。

3 个答案:

答案 0 :(得分:10)

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

更好的测试可能是:

if (unlikely(val & ~0xff)) {

if (unlikely(val & ~0x7f)) {

取决于此测试是否关心第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.");
    }
}

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

movsx   eax, BYTE PTR _val$[ebp]
cmp DWORD PTR _val$[ebp], eax
je  SHORT $LN2@RegularTes

这是掩码:

mov eax, DWORD PTR _val$[ebp]
and eax, -256               ; ffffff00H
je  SHORT $LN2@MaskTest

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

movsx   ecx, al
cmp eax, ecx
je  SHORT $LN2@RegularTes

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

test    DWORD PTR _val$[ebp], -256      ; ffffff00H
je  SHORT $LN2@MaskTest

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

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

因此,在这种情况下,答案是编写明确其意图的代码。我希望C / C ++骑师能够查看掩码并理解其意图,但如果你不喜欢它,你应该选择这样的东西:

#define BitsAbove8AreSet(x) ((x) & ~0xff)
#define BitsAbove7AreSet(x) ((x) & ~0x7f)

或:

inline bool BitsAbove8AreSet(long t){return(t& ~0xff)!= 0; } //使它成为一个好的bool    inline 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;
}

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

在我们当前的编码世界中,对于几乎每个问题领域来说,花费一点时间预先确保您的代码易于阅读和维护,而不是担心门外的性能。 / p>

答案 1 :(得分:1)

如果val为long,则(char)将除去最后8位以外的所有内容。 (长)将它投回去进行比较。

答案 2 :(得分:1)

思考:

  • 施放到char:掩盖8个低位,

  • 强制转换为:将值恢复为signed(如果char未签名)。