第十七章 关于字符串的拷贝

       今天除夕,是2013最后的一天的,也是我们公众课在今年的最后一讲,春节期间不再推送,过完春节后继续推送。所以在开讲今天内容之前,我先祝大家新年快乐,马上发财。
说到这马上发财啊,今天做了一件很糗的事,早上在QQ里面有个朋友发了一条祝福信息,我就想今天大过年的,也学学人家短信拜下年,于是就直接把那段信息给复制过来了,然后找了百十来人群发出去,后来收到大家的反馈:不要这么假吧,一点诚意都没,名字也不改的就拿出来发了,我一看不对劲连忙回去看短信,有那么三秒钟真的是石化了,差点没憋出一口鲜血来,不过最后全都化成两个——尼玛。
好了,该扯的也扯了,下面我们进入今天的正题,今天因为是除夕,所以不打算说新内容,昨天因为时间关系匆匆把代码敲上也没有验证是否能够通过,想来有些草率了,今天把我们昨天留下的代码拷贝验证了下,还真是草率了,编译没问题,但是debug运行时报错了,看了下,像是操作了一块无效内存,大家还记得昨天的代码?哦,不要回复16回去看了,因为我今天已经修改好了,下面我就把我们昨天还没修改过的代码再贴出来吧,也顺便加深大家的印象:
—————————
char* copy(char* dest, const char* src)
{
if((dest = NULL) &

&

(src = NULL))
{
printf(“无效参数。”);

return NULL;

}
char* get = dest;

memcopy(dest,src,strlen(src)+1);

return get;


}
 

char* memcopy(char* dest,const char* src,unsigned
int len)
{
 if((dest =NULL) &

&

(src = NULL))
        {
printf(“无效参数。”);

return NULL;

}
char* get = dest;

if((get+len) <= src || get >(src+len))
{
while(len–)
{
*get = *src;

get++;

src++;

}
}
else
{
get = get + len -1;

src = src + len – 1;

while(len–)
{
*get = *src;

get–;

src–;

}
}

return dest;

}
————————————————
这是我们昨天的程序,因为昨天没时间验证,今天验证下来运行时崩溃,大家现在应该看出问题出现在那里了吧,就是用黑体表示的那两句里面,说到这里,不得不说,编程真的不要细心啊,这个错误我已经连犯两次,有一次是在做一个上传下载文件的客户端和服务器上,在connect套接字的时候就是这样错将“=”当“==”使,所以导致一直无法连接服务器。那么为什么我们上面的这个会报错呢?因为我们重新初始化了指针,所以导致运行时出现无效内存。
还有,这里的逻辑判断也不对,我们的原意是要检查参数的有效性,如果有一个为空就直接返回退出,这样的话用&

&

就极不适合了,为什么会出现这个判断符呢?因为当时没考虑,就直接从下面把他复制上去的。虽然我们一般不会传无效数组指针进去,不会碰到无效退出的情况,但是什么时候自己传了个无效的指针进去不是糟糕了吗?所以上面的黑体语句应该修改为如下:
  if((dest == NULL) || (src == NULL))
再调试的话发现完全没问题了,就算有重叠内存的拷贝也不会崩溃了。下面我们来看看内存拷贝是怎么实现的。
首先,我们先要判断有没有重叠内存的可能,这个该怎么判断呢?还记得我吗上一讲的那个恒等式吗?
  array[i] == *(ptr+i);


     &

array[i] == ptr + i;


通过这两个恒等式,我们可以轻易的判断出是否有内存重叠的情况:
dest是要的目标地址,dest就是我们所需要用到的一块内存的首地址,知道首地址,我们还知道那个len(这块内存的长度),所以我们可以轻松的得到这块内存的范围:[dest,dest+len],只要我们这块内存的最小值小于等于在我们源地址的首地址,或者在源地址的最大范围外就可以肯定他们没有重叠部分,就算有重叠部分,这样也不会印象到我们一般的从低到高的拷贝,如下:
if(get<= src || get >(src+len))
当满足该条件,我们就不用担心内存重叠的问题,直接从低字节向高字节依次拷贝即可,直到将源地址里面的信息拷贝给目标内存。
——————————-
    while(len–)
     { 
             *get =  *src;


             get++;


             src++;


      
}

———————————-
这就是整个拷贝过程,当然,如果大家喜欢用数组操作的话,可以如下循环来完成复制过来:
——————————–
for(int i=0;

i<len;

i++)
    {
              get[i] = src[i];


       }
——————————-
注意一点,如果使用数组模式,不可以使用++或–,如果使用指针的话,可以这样操作。
如果不满足上面的if条件,那么就说有内存重叠的地方,这时我们就要考虑另一种拷贝方法了,我们不能从低位向高位顺序拷贝,我们要从高位向低位进行拷贝,为什么呢?就用昨天我们举的例子吧:
  copy(array1+1,array1);
从上面的原型我们可以看出,这是要把array1里面的信息拷贝到array1+1里面去,因为他们共用了一块内存:
     ——————
         ——————
像上面的这两条线一样,array1的内存块是上面那一段,array1+1是下面这一段,第一个自己可以顺利复制进array1+1里面来,就简单点说,array1这块内存里面储存的是abc,那么a顺利复制到array1+1的首地址里,那么array1+1的首地址却是array1的第二个值储存地址,也就是b的地址,这一来,b便变成了a,于是array1的最终结果是aaaarray1+1的结果也是aaa,现在明白为什么要从高位到低位的拷贝了吧。
关于这个字符串的拷贝函数就说到这里了,他到底算不算是系统string库里面的strcpy()的伪码我不知道,有空的同学可以多加验证,尤其是在效率方面,因为现在我们就差效率没有验证了,其他方面和strcpy()没有两样,甚至内存重叠拷贝的情况竟然比strcpy()的要好,我不知道这是好事还是坏事,我想微软不会没有考虑我所考虑的这些,至于效率,我也懒得去验证,留给大家去探索吧。最后祝大家合家欢乐,马上发财,马到成功。

发表评论