简介
本文将实现库函数strcpy,并将低端的写法到高端的写法进行演示讲解。使自己写的strcpy()函数不再那么low!!!
正文
我们要实现strcpy()函数,就先要了解这个函数是干什么用的;strcpy()函数是用来拷贝字符串的,在函数中有两个形参,一个源字符串,还有一个目标字符串,使用拷贝函数将源字符串中的内容拷贝到目标字符串并返回目标字符串,这样就完成了拷贝。
那么我们来实现,首先我们根据逻辑写,给形参传递源字符串的首地址和目标字符串的首地址。然后将源字符串中的字符一个一个赋给目标字符串,一直到’\0’结束。
代码如下:
//1.low逼代码
#include<stdio.h>
void my_strcpy(char *dest, char *str)
{
while(*str != '\0')
{
*dest = *str;
dest++;
str++;
}
*dest = *str;//赋值结束,将'\0'赋给目标字符串,此时的str指向的就是'\0'
}
int main()
{
char *p = "hello world";
char arr[30] = {0};
my_strcpy(arr,p);
puts(arr);
return 0;
}
这样代码就算完成了,但是这种代码其实很low,为什么呢?我们来看看下面这段代码!!!
//2.还是比较low逼的
#include<stdio.h>
void my_strcpy(char *dest, char *str)
{
while(*dest++ = *str++)
{
;
}
}
int main()
{
char *p = "hello world";
char arr[30] = {0};
my_strcpy(arr,p);
puts(arr);
return 0;
}
我们将while循环里的内容放进了判断部分,这样是不是就简洁了许多,这样的代码省去了很多麻烦,在while循环进行判断的同时顺便将赋值操作也进行了,在赋值进行到结束字符时,也就是0,条件为假,跳出循环,同时赋值也完成了,这样的代码是不是更胜一筹呢?不,这种代码还是比较low的。
当我们的源字符串为空(NULL)呢?这个时候程序就会报错!那怎么办呢?看下面这种改进!!
//较高分代码
#include<stdio.h>
#include<assert.h>
void my_strcpy(char *dest, char *str)
{
assert(str != NULL);//断言
assert(dest != NULL);
//if(str == NULL)
// return;
while(*dest++ = *str++)
{
;
}
}
int main()
{
char *p = "hello world";
char arr[30] = {0};
my_strcpy(arr,p);
puts(arr);
return 0;
}
在while循环之前加上断言assert();提前将错误杜绝在编译的时候,加上断言有助于程序猿发现这些难以发现的错误,比如,当源字符串指向空指针的时候,这个时候将空指针赋给目标字符串,那么这个时候如果不加断言,程序猿就很难发现程序中的错误。如果加上断言,那么在编译的时候,程序就会报错,并且报出错误的位置,减去了很多不必要的麻烦。
但是在这如果不用断言,而用if语句进行判断,如果源字符串指向空指针,就直接return;,这样讲道理是可以的,并且没有任何问题的,但是一个程序给用户用的话,用户用的是release版本的,我们程序猿用的是debug版本的,如果用了断言assert,那么在release版本中,编译器会自动将assert语句优化掉,但是if语句并不会优化掉,所以在release版本中if语句是一直存在的,这样断言assert的优势就显示出来了。
这样是不是就是高分代码了?算是。但是我们还能继续提升我们的这段代码的逼格。看下面
#include<stdio.h>
#include<assert.h>
void my_strcpy(char *dest, const char *str)//加上const保护不用改变的变量
{
assert(str != NULL);
assert(dest != NULL);
while(*dest++ = *str++)
{
;
}
}
int main()
{
char *p = "hello world";
char arr[30] = {0};
my_strcpy(arr,p);
puts(arr);
return 0;
}
我们在形参中源字符串前加了const,就保护了源字符串不被改变,假如我们在复制的时候不小心将目标字符串和源字符串的位置写反了,那么就是将目标字符串的内容拷贝到源字符串中了,这样就适得其反了。
在源字符串前加上const就会完美的规避这种情况,如果写反,那么编译的时候就会报错,并且让我们很快的就找到问题所在。
这样的代码没毛病,但是还是有一点点小瑕疵的,我们来看看完美的代码!!!
#include<stdio.h>
#include<assert.h>
char *my_strcpy(char *dest, const char *str)
{
char *ret = dest;//将目标空间的起始地址存在ret中
assert(str != NULL);
assert(dest != NULL);
while(*dest++ = *str++)
{
;
}
return ret;//返回目标空间的起始地址
}
int main()
{
char *p = "hello world";
char arr[30] = {0};
my_strcpy(arr,p);
puts(arr);
return 0;
}
我们可以在MSDN中搜索一下库函数strcpy()函数,结果如下
我们可以看到库函数类型并不是我们之前写的void,而是char*类型的
那我们再看看函数的返回值
我们可以看到返回的是目标字符串
我们再将这几处一修改,这样的代码就是完美的代码了,毫无瑕疵。
问题来了,为什么要用char*类型呢?为什么要有返回值呢?
答案就是为了实现链式访问,只有函数有了返回值,这样才能使用返回值进行链式访问!!!
总结
以上就是实现库函数strcpy的过程,总而言之,自己实现库函数的时候还是要看看库函数的原型是如何定义的,往原型上靠拢,写出的代码就是比较完美的,以此类推也可以实现别的库函数,比如strlen(),strcat()等等。