变态的命名
我们在写程序时,一般讲究见到变量的命名,就能让别人基本知道该变量的含义。memcpy内存拷贝,没有问题;memmove,内存移动?错,如果这样理解的话,那么这篇文章你就必须要好好看看了,memmove还是内存拷贝。那么既然memcpy和memmove二者都是内存拷贝,那二者究竟有什么区别呢?
先说memcpy
你有没有好好的参加过一场C++笔试。让你写出memcpy的实现,这是多么常见的笔试题啊。现在,拿起你的演算纸和笔;是的,是笔和纸,不是让你在你的IDE上写。写不出来?看下面吧:
void *mymemcpy(void *dest, const void *src, size_t count)
{
assert(dest != NULL || src != NULL);
char *tmp = (char *)dest;
char *p = (char *)src;
while (count--)
{
*tmp++ = *p++;
}
return dest;
}
memcpy的实现很简单,一般在笔试时,出现写源码的题目,无非就是需要注意以下几点:
1.确定函数原型;
2.判断参数合法性;
3.逻辑实现(考虑各种情况,统称逻辑实现);
4.错误处理。
当然了,我的这个没有错误处理,也不需要错误处理。上面,我写出了memcpy的实现源码,实现原理如下图所示:
![]()
这样下去,上面的代码会运行的很好,如果出现下面的情况呢?
![]()
i、n、k的内存和J、e、l的内存地址重合了,现在再使用上面的代码进行copy时,会出现什么问题呢?你有没有想过这个问题。如果没有,那就现在想想,不急着阅读下面的内容。
然后,我再留一个问题,上面的代码中,为什么都需要将void *转换成char *呢?比如:
char *tmp = (char *)dest;
可以留言回答哦。
再说memmove
memmove也是用来实现内存的直接拷贝的。说起这个命名,我个人觉的多少还是有点坑的。既然memmove也是用来内存数据移动的,那就先来看看memmove的实现源码。
void *mymemmove(void *dest, const void *src, size_t count)
{
assert(dest != NULL || src != NULL)
if (dst < src)
{
char *p = (char *)dest;
char *q = (char *)src;
while (count--)
{
*p++ = *q++;
}
}
else
{
char *p = (char *)dest + count;
char *q = (char *)src + count;
while (count--)
{
*--p = *--q;
}
}
return dest;
}
从源码看,memmove的确比memcpy复杂一些;再仔细一看,多了些什么?哦,多了一个else分支,而正是这个else分支,就处理了当src和dest的内存重合的问题。
memcpy和memmove的比较
从实现源码中的确能看出一些猫腻,当出现了src和dest的内存有重合的时机时,memmove的处理规则是从后往前进行copy。当然了,重合的问题,需要考虑的以下两种场合。
![]()
如图所示,当出现(1)对应的情况时,就需要先从src的头部开始复制;也就是memmove源码中的if分支,这部分源码和memcpy的实现是一致的;当出现(2)对应的情况时,就需要先从src的尾部开始复制,防止出现了覆盖现象。这就是memmove比memcpy多的一个考虑点,所以说,在实际使用时,使用memmove是比memcpy更安全的。
总结
总结到了这里,我觉的我已经把问题说清楚了。你说呢?如果你还有什么好的想法,欢迎你和我分享。
实验代码:
1 #include<stdio.h>
2 #include<stdlib.h>//malloc,relloc.calloc
3 #include<string.h> //memcpy.memmove
4
5 int main(int argc,char * argv[],char * envp[])
6 {
7 int i=0;
8 while(envp[i])
9 {
10 printf("%s\n",envp[i++]);
11 }//打印环境变量
12
13 char *src,*src2,*dest2;
14 char *src1,*dest1;
15 printf("请输入源字符数:\n");
16 scanf("%d",&i);
17 src=(char *)malloc(i*sizeof(char));
18
19 src1 =(char *)malloc(i*sizeof(char));
20 dest1=(char *)malloc(i*sizeof(char));
21 src2 =(char *)malloc(i*sizeof(char));
22 dest2=(char *)malloc(i*sizeof(char));
23
24 printf("请输入源字符串\n");
25 scanf("%s",src);
26
27 strcpy(src1 ,src);
28 strcpy(dest1,src);
29 strcpy(src2 ,src);
30 strcpy(dest2,src);
31
32
33 printf("这是src>dest导致重叠的情况:\n");
34 printf("memcpy :\t%s---->%s\n",src,(char *)memcpy(src2,src2+5,7) );
35 printf("memmove:\t%s---->%s\n",src,(char *)memmove(dest2,dest2+5,7) );
36 printf("这是src<dest导致重叠的情况:\n");
37 printf("memcpy :\t%s---->%s\n",src,(char *)memcpy(src1+5,src1,7) );
38 printf("memmove:\t%s---->%s\n",src,(char *)memmove(dest1+5,dest1,7) );
39 return 0;
40 }
结果如下:
XDG_SESSION_ID=3
TERM=xterm
SHELL=/bin/bash
SSH_CLIENT=192.168.153.1 3059 22
SSH_TTY=/dev/pts/0
USER=host
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:
MAIL=/var/mail/host
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
PWD=/home/host/c_workspace
LANG=en_HK.UTF-8
SHLVL=1
HOME=/home/host
LANGUAGE=en_HK:en
LOGNAME=host
SSH_CONNECTION=192.168.153.1 3059 192.168.153.128 22
LESSOPEN=| /usr/bin/lesspipe %s
XDG_RUNTIME_DIR=/run/user/1000
LESSCLOSE=/usr/bin/lesspipe %s %s
OLDPWD=/home/host
_=./test
请输入源字符数:
abcdefghijklmn
请输入源字符串
这是src>dest导致重叠的情况:
memcpy : abcdefghijklmn---->fghijklhijklmn
memmove: abcdefghijklmn---->fghijklhijklmn
这是src<dest导致重叠的情况:
memcpy : abcdefghijklmn---->abcdeabmn
memmove: abcdefghijklmn---->abcdefgmn