在第十五讲里面我们用一个copy函数来结束,让大家验证的不知道大家有没有验证,这个函数从各方面看起来都总觉得怪怪的,但是似乎该有的功能却还是有的,我之所以说是似乎,大家不要觉得奇怪,因为这真的只是似乎。
-----------------------
char* copy(char* dest, const char* src)
{
if(dest != NULL && src != NULL)
{
memset(dest,'\0',sizeof(dest));
while((*dest++ = *src++)!='\0')
;
}
return dest;
}
------------------------------
这是我们昨天定义的函数,从这几句代码里,我们看得出,这个函数是有返回值的,返回值就是传给他的第一个指针。所以我们再来验证一下这个函数的返回值。
------------------------------
char m_array[20] = "Hello World";
char m_array2[25]= "ni hao ,zhei shi shen me";
char* m_array3;
m_array3 = copy(m_array2,m_array);
--------------------------------
大家是不是觉得m_array3的值和m_array2一样的呢?为了验明正身,我们不妨将结果打印出来看看:
嗯,为什么结果不和想象中的呢?这不是我们想要的啊,我们想要的是m_array2 = m_array3,为什么会有这种奇怪的结果呢?好吧,我们现在再回头来研究一下这个函数。
现在看来, memset(dest,'\0',sizeof(dest)); 并没有起到真正的作用,干脆把他去掉吧。
接下来我们看到的是拷贝过程,这个过程是通过while循环来控制的呢,直到遇到src的结尾符'\0'终止,于是我们在这里便得到了为什么我们的m_array2能够得到正确的答案的解释了。但是接下来return dest。不过这时的dest已经不是再最初的那个dest,也不是我们刚才得到的那个dest,这个dest是上面循环用剩下的,那么要怎么才能实现返回的和得到的和我们拷贝的是同一个数据呢?这就是我们这里要说的指针了。
为什么说指针是C/C++的高阶知识呢?为什么会说C++是面对对象编程中唯一一门在内存上操作的编程语言?他是怎么实现在内存上操作的呢?这就是我们这里所要说的指针,指针就是指向内存块的东东,所以我们要在内存上操作,就得依靠指针。指针既然这么重要,那么我们要就来说一下指针。
怎么声明一个指针?
int* ptr;
就这么简单,“*”是指针标识符,声明一个指针很简单,和声明一个数据变量没有两样,唯一的不同点就是在变量前添加这个”*“,这样普通变量就变成了该类型的指针。
int a = 6;
int *ptr = &a;
上面的两个声明,第一句声明的是一个变量,第二句是声明一个指针,并且让他指向变量a的地址。
想要取某个变量的地址,直接在该变量前添加”&“符号即可,所以对变量取地址同样是也相当于获取一个指针。
大家可以试着用printf("ptr = %p \n.*ptr = %d",ptr,*ptr)看看输出的是什么结果。
ptr的值根据不同系统会得到不同的值,这就是a所存放的地址,而*ptr却是这个地址里面所存放的数据6。
说到这里,大家是不是想问,指针和数组有什么关系呢?在copy函数里我们声明的明明是指针,为什么最后却又用数组来作为参数呢?这就是现在我们所要说的。
上一讲我们说到数组是存放在一连串的内存之中,那么这段内存怎么得到呢?我们是要用像上面那样取地址表示吗?当然可以,但是没有那个必要,因为在数组中,数组名就相当于一个指向这段连续的首地址的指针,所以我们这就是我们为什么用数组名去作为copy函数的参数的原因。
在这里给大家说一个关系式,大家只要记住这个关系式就能够轻松在数组和指针之间相互转换了:
array[i] == *(ptr+i);
&array[i] == ptr+i;
这是两个恒等式,如果不明白指针和数组的关系时可以再次来查看这一讲。
指针说到此处,也差不多了,对昨天我们的copy函数现在是不是已经明了了呢?既然都明白了,现在我们来改进这个函数。我们声明一个指针,然后同样让他指向dest的地址。
char* get = dest;
在最后我们返回get的地址就好。
return get;
-----------------------------
char* copy(char* dest, const char* src)
{
if((dest = NULL) && (src = NULL))
{
printf("参数不能空。");
return ;
}
char* get = dest;
while((*dest++ = *src++)!='\0')
;
return get;
}
--------------------------------
现在我们同样用昨天的main()来调试这个函数,发现没问题了,m_array2 和 m_array3的结果相同了,我们多试试几个都没问题,这是不是表示已经完美了呢?可以直接代替strcpy()了呢?当然能不能我们不知道,但是从我们目前的测试中可以看出,基本的功能已经满足了,那我们还在怀疑什么呢?
我们再来看看这个函数,他似乎遗漏了一个问题,当然这个问题通常都是我们会回避的,但回避就不代表真正的解决问题了,这个问题便是如果这两个指针都同时指向一块内存块呢?那岂不是糟糕了呢?如同下面一样:
copy(m_array+1,m_array);
当我们执行这句代码时,程序崩溃了。我们在用string.h里面的strcpy来试试:
strcpy(m_array1+1,m_array);
再执行程序,发现毫无问题,既然这个函数没问题,那么有问题的就是我们自定义的copy()函数了, 哎,看来想要编程真不是一件简单的事啊,很多人都以为这是strcpy的伪代码,但是他们都忽略了一个问题,重复内存的拷贝。
那么怎么实现重叠内存的拷贝呢?我们再来定义一个函数:memcopy();
------------------------------------
char* memcopy(char* dest,const char* src)
{
if((dest = NULL) && (src = NULL))
{
printf("无效参数。");
return;
}
char* get = dest;
int len = strlen(src)+1;
if(dest <= src || dest >= (src+len))
{
while(len--)
{
*dest = *src;
dest++;
src++;
}
}
else
{
dest = dest + len -1;
src = src + len -1;
while(len--)
{
*dest = *src;
dest--;
src--;
}
}
return get;
}
----------------------------------------------
然后我们在copy里面调用上面的memcopy函数:
------------------------------
char* copy(char* dest, const char* src)
{
if((dest = NULL) && (src = NULL))
{
printf("参数不能空。");
return ;
}
char* get = dest;
memcopy(dest,src);
return get;
}
------------------------------------
这似乎看上去才有些像strcpy()伪码,但是到底是与不是还得靠大家去验证,因为今天太晚了,我也没验证他的可行性,改天有时间我亲自验证一下是否可行,如果有误,大家请注意后续内容,一般会在下一讲会纠正,今天再不推送就浪费次数了。