2

所以我今天早些时候浏览了 Quake 引擎的源代码,偶然发现了一些编写的实用函数。其中之一是“Q_memcpy”:

void Q_memcpy (void *dest, void *src, int count)
{
    int             i;

    if (( ( (long)dest | (long)src | count) & 3) == 0 )
    {
        count>>=2;
        for (i=0 ; i<count ; i++)
            ((int *)dest)[i] = ((int *)src)[i];
    }
    else
        for (i=0 ; i<count ; i++)
            ((byte *)dest)[i] = ((byte *)src)[i];
}

我了解该函数的整个前提,但我不太了解源地址和目标地址之间按位或的原因。所以我的问题总和如下:

  • 为什么 'count' 会在同一个按位算术中使用?
  • 如果结果不同,为什么要检查结果的最后两位?
  • 整个检查的目的是什么?

我确信这是显而易见的,但请原谅我的无知,因为在编程方面我还没有真正深入研究更底层的事情。我只是觉得它很有趣,并想了解更多。

4

3 回答 3

3

它正在找出源指针和目标指针是否int对齐,以及是否count是精确int的字节大小。

如果这三件事都是真的,那么它们的 ls 2 位都将是0(假设指针和int是 4 个字节)。因此该算法对三个值进行 OR 运算,并隔离 ls 2 位。

int在这种情况下,它通过复制int。否则它会复制char.char

如果测试失败,更复杂的算法将复制一些前导字节和尾随字节char以及char中间字节intint

于 2018-05-23T18:35:42.023 回答
2

与 3 的按位 ORing 和 ANDing 是检查源、目标和count是否可以被 4 整除。如果是,则该操作可以使用 4 字节字,而此代码假定int为 4 字节。否则按字节执行操作。

于 2018-05-23T18:34:51.217 回答
1

它首先测试所有 3 个参数是否都可被 4 整除。如果 - 且仅当 - 它们都是,它会一次复制 4 个字节。

即这个未解码的将是

if ((long) src % 4 == 0 && (long) dst % 4 == 0 && count % 4 == 0 )
{
    count = count / 4;
    for (i = 0; i < count; i++)
        ((int *)dest)[i] = ((int *)src)[i];
}

我不确定他们是否测试了他们的编译器,它甚至为测试生成了错误的代码,因此他们决定以如此复杂的方式编写它。在任何情况下,如果位 n 设置在或中的任何一个中,x | y | z则将保证在结果中设置位n。因此,如果结果为 0,则没有一个数字设置了 2 个最低位中的任何一个,因此可以被 4 整除。xyz(x | y | z) & 3


当然,现在使用会相当愚蠢——最近的库实现中的标准库memcpy几乎肯定比这更好。

因此,在最近的编译器上,您可以通过将所有调用Q_memcpy切换到memcpy. GCC 可以生成诸如 64 位或 SIMD 移动之类的东西,memcpy具体取决于要复制的区域的大小。

于 2018-05-23T18:34:37.693 回答