1 strcpy
1.1 strcpy的功能
用于将字符串从一个地方拷贝到另外一个地方。
1.2 strcpy函数C语言源码:
char * strcpy(char* dst,const char *src)
{
if((src==NULL)||(dst==NULL))
return NULL;
char *strdst=dst;
while((*dst=*src)!="/0");
return strdst;
}
1.3需要特别注意的地方:
1.3.1 为什么要有函数返回值,并且返回值类型为char *?
答:首先必须要有返回值,因为字符串拷贝并不一定成功,比如传递的参数有问题,那么就必须通过返回特定的值进行查错;其次,拷贝成功后也要有返回值,肯定不应该是返回源地址值,因为它始终没有变过,返回它毫无意义。返回目的地址才是最合理的,因为把字符串从一个地方(src指向的地方)拷贝到另外一个地方(dst指向的地方)后接下来一般都会访问dst指向的地方,那么把dst的值返回给另外一个指针变量岂不是更加合理,例如:
char table[100];
char *buf = strcpy(table,"shenzhen");
此后就可以通过buf来访问table了。
1.3.2 为什么函数列表的第二个形式参数要用const修饰?
答:用const修饰就可以阻止src指向的区域被有意无意修改,const修饰*src,即修饰src指向的区域,所以对该区域进行写保护。
1.3.3 为什么要进行参数合法性检查?为什么不写成if((!dst)||(!src))?
答:①如果src的值为NULL,那么就会把一个非法区域的数据拷贝到dst指向的缓冲区中,而这很容易造成内存越界(因为很有可能很晚才遇到"/0"标记符或者无法遇到"/0"标记符);如果dst的值为NULL,那么就相当于把一串数据拷贝到一些非法区域,从而造成系统崩溃。②因为dst和src类型为char*,并不是BOOL类型,虽然if((!dst)||(!src))可以进行从char*到BOOL的隐形类型转换,但是这相当于把可靠性交给了第三方去维护,显然不是一个合格程序员的素养。
1.3.4 为什么while循环里面要判断!="/0"?为什么while循环不写成while (*strSrc!='\0') *dst++=*src++;?
答:①字符串都是以"/0"作为结束标志的,即便你写一个字符串"shenzhen",系统也会自动在结尾增加"/0",只是你看不到而已。②这样写就会导致字符串的"/0"无法拷贝到目的区域,这是不行的。
1.4 该函数有什么缺陷
①如果你传递进来的src指向的不是字符串,而是其他空间,那么会很容易造成内存越界。比如
char table1[10];
int table2[11]={1,2,3,4,5,6,7,8,9,10,11}
strcpy(&table1,&table2);
由于table2中存放的并不是字符串,因此在程序检测到"/0"这个字符串结束符之前,它会继续将table2中的数据拷贝到table1中,从而导致内存越界。
1.5 总结
一个简单的函数的编写足以窥视一个程序员的水准。函数功能主体设计固然很重要,但是函数返回值的设计、函数参数的安排、参数关键字的修饰(比如const、volatile等等)以及入口参数合法性检查,这些都是不是细节问题,而同样是函数主体的一部分,有时甚至更为关键。
2 memcpy
2.1 memcpy的函数功能
用于将指定长度的数据从一个地方拷贝到另外一个地方
2.2 memcpy函数的C语言源码:
void *memcpy(void *dst,const void *src,unsigned int len)
{
if((NULL==dst)||(NULL==src))
return NULL;
char *Strdst = (char *)dst;
while(len--)*dst ++=*src++;
return Strdst;
}
2.3 需要注意的地方
①函数返回值为什么是void *?
答:由于memcpy拷贝的数据并不限于字符串,理论上可以是指向任何类型的数据指针,因此这里设计为void *类型。
②函数形式参数为什么是void *?
答:memcpy拷贝的数据可以是任何类型的数据,所以这里采用void *类型。
3 strcpy和memcpy对比
3.1 strcpy只用于字符串拷贝,并且是任意长度的字符串拷贝;memcpy是通用型的拷贝,并且指定拷贝长度。
3.2 两者都要有返回值,并且返回值是目的地址指针