目录
- 前言
- strcpy实现的基本原理
- 函数的模拟实现
- 代码优化
- assert--断言
- const关键字
- strcpy的返回值
- 结语
前言
本章内容我们将通过相关函数来实现库函数中的strcpy。
strcpy实现的基本原理
C语言 strcpy()函数用于对字符串进行复制(拷贝)。需要的头文件为 <string.h>。原理如下
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的模拟实现流程,如有出入,欢迎指正。