目录
- 前言
- strcpy实现的基本原理
- 函数的模拟实现
- 代码优化
- assert–断言
- const关键字
- strcpy的返回值
- 结语
前言
本章内容我们将通过相关函数来实现库函数中的strcpy。
strcpy实现的基本原理
C语言 strcpy()函数用于对字符串进行复制(拷贝)。需要的头文件为 。原理如下
char* strcpy(char* strDestination, const char* strSource);
其中的strSource为源字符串,strDestination为目的字符串,strcpy的作用就是将 strSource 指向的字符串复制到 strDestination。
我们举个例子
int main(){char arr1[20] = "xxxxxxxxxxxxxxx";char arr2[] = "hello";strcpy(arr1, arr2);printf("%s\n", arr1);return 0;}
打印结果如图1
我们知道字符串arr2实际上是”hello\0“,那么我们在打印字符串的时候有没有打印这个\0呢,我们用监视的方法来看一下,如图2
我们发现实际上\0也是被拷贝过来了的。
函数的模拟实现
我们来写一个函数my_strcpy实现字符串的拷贝,框架如下
void my_strcpy(char* dest, char* src){}int main(){char arr1[20] = "xxxxxxxxxxxxxxx";char arr2[] = "hello";my_strcpy(arr1, arr2);printf("%s\n", arr1);return 0;}
当’\0’也被拷贝过去时函数停止运行,我们可以用一个循环来实现代码
void my_strcpy(char* dest, char* src){while (*src != '\0'){*dest = *src;dest++;src++;}//当src解引用为'\0'时跳出循环,但'\0'还并没有被复制过去*dest = *src;//将最后的'\0'也复制过去}int main(){char arr1[20] = "xxxxxxxxxxxxxxx";char arr2[] = "hello";my_strcpy(arr1, arr2);printf("%s\n", arr1);return 0;}
代码优化
上述函数只是我们按照基本思路一步步写出来的,肯定不是最优解,看下面这段代码
void my_strcpy(char* dest, char* src){while (*dest++=*src++){;}}
这段代码才是大师级别的书写格式,它将我们上述的个步骤融为一体,下面我们来分析这段代码。
首先我们将解引用的src的值赋给dest,然后后置++,各自都跳到下一个位置再进行赋值,当最后一次src解引用为’\0’并赋值给dest时,由于’\0’的ascll码值为0,当0被赋值给dest,判断结果也为0,0为假,就会跳出循环,这样既把\0复制了过去,还借此跳出了循环,可谓是一举两得。
assert–断言
假设arr1或arr2为空指针会发生什么呢” />空指针是不能解引用的,程序会报错。可是这种情况也是难以避免的,为了避免程序崩溃,我们在函数开头添加一个assert(断言),通过一个判断来选择继续执行或中止代码。
void my_strcpy(char* dest, char* src){assert(src != NULL);//若为真则继续执行,若为假则终止代码while (*dest++=*src++){;}}int main(){char arr1[20] = "xxxxxxxxxxxxxxx";char arr2[] = "hello";my_strcpy(arr1,NULL);printf("%s\n", arr1);return 0;}
我们假定arr2为空指针,代码运行结果就会如下
这样,我们既可以有效防止程序崩溃,还可以很清晰的在代码中锁定出问题的地方(比如途中出问题的地方就为源.c line27)。
const关键字
如果我们在函数书写中把拷贝对象和被拷贝对象写反了,会发生什么呢?
void my_strcpy(char* dest, char* src){assert(src != NULL);while (*src++=*dest++){;}}int main(){char arr1[20] = "xxxxxxxxxxxxxxx";char arr2[] = "hello";my_strcpy(arr1,arr2);printf("%s\n", arr1);return 0;}
代码运行结果如图5
我们会发现程序崩溃了,原因在于arr2字符串长度放不下arr1。但如果我们加入const关键字修饰就能很好的规避这个问题。
const关键字的作用在于修饰变量,这个变量就被称为常变量,它具备了常量的属性,但本质上还是一个变量。
但我们来看一段代码
int main(){const int num = 10;int* p = #*p = 20;printf("%d\n",num);}
运行结果如图6
我们发现,被const修饰的变量num还是被修改了,我么要想保持num不变,就需要修饰指针变量
int main(){int num = 10;const int* p = #*p = 20;printf("%d\n",num);}
这样* p=20这段代码将无法实行。这是因为const如果放在* 左边,修饰的是*p,表示的是指针指向的内容,是不能通过指针来改变的,但是指针变量本身( p )是可以修改的,如果放在 *右边,修饰的就是p,和放在左边完全相反,指针变量本身将无法被修改,但指针指向的内容却可以被修改,num还是会被修改为20
strcpy的返回值
strcpy这个库函数的执行原理实际上是会返回目标空间的起始地址。
char* my_strcpy(char* dest, const char* src){assert(src != NULL);assert(dest != NULL);char* ret = dest;while (*dest++=*src++){;}return ret;}int main(){char arr1[20] = "xxxxxxxxxxxxxxx";char arr2[] = "hello";//目标空间的起始地址 源空间的起始地址my_strcpy(arr1,arr2);printf("%s\n", my_strcpy(arr1,arr2);//链式访问return 0;}
结语
以上就是标准的库函数strcpy的模拟实现流程,如有出入,欢迎指正。