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

标签 c++ casting

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

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

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

最佳答案

这是一种检查高位是否有垃圾的廉价方法。高 8、24 或 56 位的字符转换(取决于 sizeof(val)),然后将其提升回来。如果 char 被签名,它也会对 extend 进行签名。

更好的测试可能是:

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

在发行版中,我得到了这个 Actor 代码:

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

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

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

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

哪个更快?打败我——坦率地说,除非你在一个非常慢的 CPU 上运行这种测试,或者运行它数十亿次,否则这无关紧要。一点也不。

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

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

或:

内联 bool BitsAbove8AreSet(long t) { return (t & ~0xff) != 0; }//让它成为一个 bool 值 内联 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() 实际上是一段将被执行数十亿次的内联代码。在这种情况下,我非常关心不进行函数调用、不做冗余工作、不重新检查值、确保计算流程将转化为能够明智地使用寄存器的东西等等。但我真正主要关心的是代码可以被下一个必须看它的可怜的笨蛋(可能是我)阅读。

在我们当前的编码世界中,对于几乎每个问题领域来说,预先花一点时间确保您的代码易于阅读和维护比担心性能问题要便宜得多。

关于c++ - 连续应用两个显式类型转换的原因是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5118976/

相关文章:

c++ - 获取模板中变量的类型

c++ - Flood Fill递归堆栈溢出

c++ - 字长是否限制内存地址的数量,而不是内存本身?

java - 列表<Map>到对象[]

c# - 如何将 List 转换为继承自 List<T> 的类型?

javascript - OO Javascript 是否有 `to_bool` 或 `__len__` 等效项?

c++ - 使用Windows API创建快捷方式.lnk

c++ - 使用按钮为C++创建计数器

mysql - 使用 BIT 作为 INT 以提高存储容量效率

c++ - C int 到 char 转换添加了垃圾字符