理解memcpy()的源代码

时间:2013-07-11 11:00:29

标签: c

00018 void *memcpy(void *dst, const void *src, size_t len)
00019 {
00020         size_t i;
00021 
00022         /*
00023          * memcpy does not support overlapping buffers, so always do it
00024          * forwards. (Don't change this without adjusting memmove.)
00025          *
00026          * For speedy copying, optimize the common case where both pointers
00027          * and the length are word-aligned, and copy word-at-a-time instead
00028          * of byte-at-a-time. Otherwise, copy by bytes.
00029          *
00030          * The alignment logic below should be portable. We rely on
00031          * the compiler to be reasonably intelligent about optimizing
00032          * the divides and modulos out. Fortunately, it is.
00033          */
00034 
00035         if ((uintptr_t)dst % sizeof(long) == 0 &&
00036             (uintptr_t)src % sizeof(long) == 0 &&
00037             len % sizeof(long) == 0) {
00038 
00039                 long *d = dst;
00040                 const long *s = src;
00041 
00042                 for (i=0; i<len/sizeof(long); i++) {
00043                         d[i] = s[i];
00044                 }
00045         }
00046         else {
00047                 char *d = dst;
00048                 const char *s = src;
00049 
00050                 for (i=0; i<len; i++) {
00051                         d[i] = s[i];
00052                 }
00053         }
00054 
00055         return dst;
00056 }

我刚刚完成了memcpy的实现,以了解它与使用循环的不同之处。但是我在使用循环而不是memcpy之间看不出任何区别,因为memcpy在内部再次使用循环来复制。

我无法理解他们为整数做的if部分 - i < len/sizeof(long)。为什么要进行此计算?

6 个答案:

答案 0 :(得分:14)

  

我无法理解他们是否为整数所做的部分。我&lt; LEN /的sizeof(长)。为什么要进行此计算?

因为它们是复制单词,而不是单个字节,在这种情况下(正如评论所说,它是一种优化 - 它需要更少的迭代,CPU可以更有效地处理字对齐的数据)。

len是要复制的字节数sizeof(long)单个字的大小,因此要素数量为copy(表示要执行的循环迭代)是len / sizeof(long)

答案 1 :(得分:6)

  

了解它与使用循环的不同之处。但我不能   使用循环而不是memcpy的区别,因为memcpy使用循环   再次在内部复制

然后它使用循环。也许libc的其他实现不会那样做。无论如何,如果它确实使用了循环,那么问题/问题是什么?另外,正如您所看到的那样,它不仅仅是一个循环:它会检查对齐并根据对齐方式执行不同类型的循环。

  

我无法理解他们是否为整数所做的部分。我&lt;   LEN /的sizeof(长)。为什么要进行此计算?

这是检查内存字对齐。如果目标和源地址是字对齐的,并且长度副本是字大小的倍数,则它按字(long)执行对齐复制,这比使用字节(char更快),不仅因为大小,而且因为大多数架构更快地进行字对齐复制。

答案 2 :(得分:5)

len%sizeof(long)会检查您是否正在尝试复制不属于long的完整长片。

00035    if ((uintptr_t)dst % sizeof(long) == 0 &&
00036             (uintptr_t)src % sizeof(long) == 0 &&
00037             len % sizeof(long) == 0) {
00038 
00039                 long *d = dst;
00040                 const long *s = src;
00041 
00042                 for (i=0; i<len/sizeof(long); i++) {
00043                         d[i] = s[i];
00044                 }

检查对齐,如果为true,则一次快速复制(sizeof(long)个字节)。

00046    else {
00047                 char *d = dst;
00048                 const char *s = src;
00049 
00050                 for (i=0; i<len; i++) {
00051                         d[i] = s[i];
00052                 }
00053    }

这是针对错误对齐的数组(慢速复制(一次1个字节))

答案 3 :(得分:4)

for (i=0; i<len/sizeof(long); i++) {
    d[i] = s[i];
}

在这个for循环中,每次复制long时,都要复制len的总大小,这就是它需要i<len/sizeof(long)作为终止循环的条件的原因。

答案 4 :(得分:0)

  

我只是通过memcpy的实现来了解它与使用循环的不同之处。但是我看不到使用循环而不是memcpy之间有什么区别,因为memcpy再次在内部使用循环进行复制。

循环(控制语句)是与if(决策语句)相邻的基本元素之一,而其他几乎没有这样的东西。因此,这里的问题不是关于普通循环和使用memcpy之间的区别。

memcpy可以通过为您提供随时可用的API调用来帮助您完成任务,而不是让您为琐碎的事情编写20行代码。如果愿意,您可以选择编写自己的代码来为您提供相同的功能。

前面已经指出的第二点是,它在long数据类型和其他类型之间提供了优化。因为在long中,它是一次复制一个数据块,这就是我们所说的单词,而不是逐字节复制,这会花费更长的时间。如果执行的操作很长,则需要进行8次迭代才能完成 ,memcpy 一次复制一次单词即可完成一次迭代。 >

答案 5 :(得分:0)

好像您看到了memcpy的汇编代码,它表明在32位系统中,每个寄存器都是32位,它一次可以存储4个字节,如果仅在32位寄存器中复制一个字节,则CPU需要额外的指令周期。

如果len / count是4的倍数,我们可以在一个周期内复制4个字节

    MOV FROM, R2
    MOV TO,   R3
    MOV R2,   R4
    ADD LEN,  R4
CP: MOV (R2+), (R3+) ; "(Rx+)" means "*Rx++" in C
    CMP R2, R4
    BNE CP