2

我在http://androidxref.com/4.0.4/xref/bionic/libc/string/bcopy.c中阅读了 memcpy 实现, 发现以下代码很难理解,谁能解释一下?

 36 /*
 37  * sizeof(word) MUST BE A POWER OF TWO
 38  * SO THAT wmask BELOW IS ALL ONES
 39  */
 40 typedef long word;      /* "word" used for optimal copy speed */
 41 
 42 #define wsize   sizeof(word)
 43 #define wmask   (wsize - 1)
 44 

...

        /*
 78          * Copy forward.
 79          */
 80         t = (long)src;  /* only need low bits */
 81         if ((t | (long)dst) & wmask) {
 82             /*
 83              * Try to align operands.  This cannot be done
 84              * unless the low bits match.
 85              */
 86             if ((t ^ (long)dst) & wmask || length < wsize)
 87                 t = length;
 88             else
 89                 t = wsize - (t & wmask);

这些按位运算是什么意思?他们的意图是什么?

4

2 回答 2

7

基本思想是满足对齐约束:每次要复制一个单词的每个“单词”必须在“单词”边界上对齐。

一些 CPU 将此作为基本约束,即加载和存储必须发生在“自然”边界上。在较旧的 ARM 处理器上,地址的低位实际上完全被忽略,因此从奇值地址加载或存储两个字节具有与以下相同的效果:

short w = *(short *)(addr & ~1);

例如。在其他一些 CPU 上,未对齐的加载或存储会导致陷阱(例如 MIPS 和 SPARC),还有一些 CPU 会这样做,但会降低性能(x86)。

因此,假设您要从地址 0x12345 复制大量字节(例如,其中 4096 个)到地址 0x22345,并且“字长”为 4 个字节。如果我们首先复制三个字节,地址现在将是 0x12348 和 0x22348。此时我们可以只复制 1023 个 4 字节字,一次一个字,而不会因任何对齐问题而绊倒。之后我们将有一个剩余的字节要复制,因为 4096 = 3 + (4 * 1023) + 1。

这一切都假设字节都是单独寻址的,即使在加载和存储“单词”时也是如此。这种假设在某些机器上是错误的:例如,旧的 Data General MV10000 CPU 将使用“字地址”来寻址“字”,这实际上是字节地址除以 2。(因此不可能对跨越两个字节的“字”进行寻址:位置 0 处的字具有字节地址 0 和 1,但字地址为 0;位置 1 处的字具有字节地址 2 和 3;位置 2 处的字具有字节地址 4 和 5;等等。)在这样的机器上,您可能需要使用不同版本的 bcopy.c。

As @Alex noted, the XOR is just making sure that it is actually possible to align the two addresses. If you're copying from 0x12345 to 0x22345, it is; but if you're copying from 0x12345 to 0x22344, the two addresses will never line up.

于 2012-06-28T04:32:16.293 回答
6

只需一步一步地做:

t = (long)src;
if ((t | (long)dst) & wmask)

这些检查是否至少有一个src并且dst不是sizeof(long).

if ((t ^ (long)dst) & wmask || length < wsize)

这将检查srcdst是否以不同方式对齐sizeof(long)(IOW,不是 的“相等”倍数sizeof(long))或 if length < sizeof(long)-1

最后,您收到t必须在未对齐位置之间复制多少字节作为字节,要么全部(length),要么刚好足够(小于sizeof(long))到达地址的倍数,sizeof(long)其余的可以以 . 为单位复制long。而后者是速度优化。

要查看所有内容,您必须知道整数,当以二进制表示时,是某个 2 的幂的倍数,而其最低有效位低于 2 的幂时全为零。

例子:

100 2 (4 10 ) 是 100 2 (4 10 ) 的倍数1100 2 ( 12
10 )100 2 (4 10 ) 的倍数10000 2 ( 16
10 )100 2 ( 4 10 )
0 2的倍数(0 10 ) 是 100 2的倍数(4 10 )
11 2 (3 10 ) 不是 100 2 (4 10 )
1101 2 (13 10 ) 的倍数) 不是 100 2 (4 10 )的倍数

这就是& (sizeof(long)-1)用途。

您还需要知道一个值XORed本身给出 0,并且当您使用XOR不同的值时,结果是非零的。所以可以XOR用来比较。这就是(t ^ (long)dst)目的。

于 2012-06-28T04:08:02.163 回答