23
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)。为什么需要这个计算?

4

6 回答 6

18

我不明白他们是否为整数做部分。i < len/sizeof(long)。为什么需要这个计算?

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

len是要复制的字节数sizeof(long),并且是单个 word 的大小,因此要复制的元素数(意味着要执行的循环迭代)是len / sizeof(long)

于 2013-07-11T11:04:37.433 回答
6

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                 }

检查对齐,如果为真,则快速复制(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 个字节))

于 2013-07-11T11:07:31.963 回答
6

了解它与使用循环有何不同。但是我使用循环而不是 memcpy 没有任何区别,因为 memcpy 在内部再次使用循环来复制

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

我不明白他们是否为整数做部分。i < len/sizeof(long)。为什么需要这个计算?

这是检查内存字对齐。如果目的地址和源地址是字对齐的,并且复制的长度是字大小的倍数,那么它会按字( )执行对齐的复制,这long比使用字节(char还因为大多数架构执行字对齐副本的速度要快得多。

于 2013-07-11T11:06:13.763 回答
4
for (i=0; i<len/sizeof(long); i++) {
    d[i] = s[i];
}

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

于 2013-07-11T11:04:55.533 回答
0

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

循环(控制语句)是与 if(决策语句)和其他一些类似事物相邻的基本元素之一。所以这里的问题不在于正常循环和使用之间有什么区别memcpy

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

前面已经指出的第二点是,它提供的数据类型与其他类型之间的优化。long因为long它一次复制一个数据块,我们称之为一个字,而不是一个字节一个字节地复制,这需要更长的时间。在 long 的情况下,需要 8 次迭代才能完成的相同操作,通过memcpy 一次复制单词在单次迭代中完成。

于 2019-06-21T09:21:43.613 回答
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
于 2019-12-26T08:57:06.350 回答