一次关于 memmove 实现的记录
有一次面试嵌入式工程师,题目中有一题为“请不使用任何函数实现 memmove 源码”,这题我没做出来。
没做出来的原因1:我没使用过 memmove
函数不知道其作用,只知道字面意思。
没做出来原因2:就算面试官告诉我这个函数的作用是把一段内存从一个地方移动到另一个地方,我也不知道如何不用函数来实现。
关于这道题我脑子里第一反映就是使用 memcpy
直接拷过去,但是规定是不能使用函数的,我就傻了。后面我就想要不新开一段内存,做两次复制,第一次复制是源内存中数据到新内存空间,第二次复制是新内存空间到目标地址空间。这样做不是不行,但是浪费内存空间,而且操作步骤多,浪费处理器计算力。后面面试官又提醒我说,不要开辟新的内存空间该怎么办?我回答我实在没有办法了……
直接我现在看了 gcc 里的 memmove
的源码现在才知道自己面试不过是应该的。先看下 gcc 中的源码吧。
void *
memmove (void *dest, const void *src, size_t len)
{
char *d = dest;
const char *s = src;
if (d < s)
while (len--)
*d++ = *s++;
else
{
char *lasts = s + (len-1);
char *lastd = d + (len-1);
while (len--)
*lastd-- = *lasts--;
}
return dest;
}
现在才知道原来标准库里面的函数十分朴实,没有什么花里胡哨的操作,就是一个字节一个字节的拷贝。但是拷贝也是有前提的,如果目标地址低于源地址,就直接拷贝直接 len 为 0,这样拷贝的顺序就是正向的;如果目标地址是高于源地址的,那就需要做逆向拷贝,从最后一个字节拷贝到第一个字节来,这样做的原因就是为了避免正向拷贝的情况下,目标地址其实位置是源地址内存中的一部分,那么就会有内存覆盖情况,最终覆盖部分数据丢失,而逆向拷贝就不会。
后来我也看了 memcpy
的源码,它其中就有内存覆盖的情况,所以当使用 memcpy
函数时,如果你的目标地址的起始位置是源地址空间的一部分,就会出现错误。
src
↓
+-------------------------------------------------+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
+-------------------------------------------------+
| a | b | c | d | e | f | g | h | i | j | k | l |
+-------------------------------------------------+
↑
dest
上面就是 memcpy 出现内存覆盖而导致数据丢失的情况,所以我们使用 memcpy 的时候应该知道有这么种情况,即使是数据丢失也是我们期望的一部分话就不用担心该问题了。