一、strcpy函数
1.将基本的功能实现:把目标数组的内容换成源数组的内容。
void my_strcpy (char* dest,char* src)
{
//字符串内容的拷贝
while (*src != '\0') {
*dest = *src;
dest++;
src++;
}
//\0的拷贝
*dest = *src;
}
思路:(1).字符串内容的拷贝:将源数组的第一个字符赋值给目标数组的第一个,之后源数组和
目标数组的指针向后挪一位,直到当移动到src指向\0时,不满足循环条件,退出循环。此时已经
将字符串内容部分全部拷贝完成,但是由于*src==‘\0’时没有进入循环,所以没有将\0赋值给目标
数组,需要单独将\0赋值给目标数组。
(2).\0的拷贝:现在*src==‘\0‘,所以直接将*src赋值给*dest即可。
2.简单优化
void my_strcpy (char* dest,char* src)
{
//字符串内容的拷贝
while (*src != '\0') {
*dest++ = *src++;
}
//\0的拷贝
*dest = *src;
}
思路:原本是先解引用在++,所以可以合并成后置++,即*dest++ = *src++ ==> 做的是先解引
用,后++,和1中的循环体内的代码等价。
3.进一步优化
void my_strcpy (char* dest,char* src)
{
//实现了整个字符串的拷贝
while ((*dest++ = *src++)) {
;
}
}
思路:将*dest++ = *src++放到循环判断条件里,就可以将整个字符串拷贝过去。
原因:例如刚开始时,将源数组的首元素赋值给目标数组,循环判断条件就为*dest的值,一定
不为0,所以为真,循环会继续······ 直到当src指针挪到\0时,这时将\0赋值给*dest,判断条
件的值为0(\0的ASCII值为0),判断为假,循环结束,但是\0也赋值给了*dest,所以就完成了
整个字符串的拷贝。
4.优化为易于调试的代码 -- assert断言 -- 使用头文件#include <assert.h>
assert():用于在调试过程中捕捉程序错误。
括号内如果为真,则程序继续运行;为假,程序停止运行,并报错。
void my_strcpy (char* dest,char* src)
{
assert(src != NULL);
assert(dest != NULL);
//实现了整个字符串的拷贝
while ((*dest++ = *src++)) {
;
}
}
思路:进入函数首先利用assert,判断传过去的dest和src是否为空指针。如果是NULL,程序停
止运行,并报错;如果不是,程序继续运行。
5.进一步优化为易于调试的代码
(1)回顾const:const修饰变量,这个变量就被称为常变量,不能被修改,但是本质上还是变量
但是观察这段代码发现:num是const修饰的变量,理论上来说不能被修改。但是通过找到num
的地址从而改变了num的值。这种操作违反了const不能被修改的本意。
(2)想要实现真正的不能被修改:介绍const修饰指针变量
int* p有两个放const的位置:const int* p和int* const p
(a)const放在*的左边(const int* p):修饰的是*p,表示指针指向的内容,是不能通过指针来改
变的;但是指针变量本身是可以修改的。
这时*p即num的值不可以改变。(达到const的本意:不可修改值)
但是p的值(地址)可以改变的。
(b)const放在*的右边(int* const p):修饰的是指针变量p,表示指针变量不能被改变;但是指针
指向的内容,可以被改变。
这时p的值(地址)不可以改变。 (没有达到const的本意:不可修改值)
但是*p即num的值可以被改变了。
(c)const int* const p:p不可以改变,*p也不可以改变。
strcpy函数:把src指向的内容拷贝放进dest指向的空间中。从本质上讲,希望dest指向的内容被
修改,src指向的内容不应该被修改。
现在利用const修饰指针变量来优化,来保护 *src 不被修改。
void my_strcpy (char* dest,const char* src)
{
assert(src != NULL);
assert(dest != NULL);
//实现了整个字符串的拷贝
while ((*dest++ = *src++)) {
;
}
}
6.实现与库函数strcpy()基本相同的结果
库函数strcpy()的返回值类型是char* ,返回的是目标空间的起始地址。这样做的好处是可以实现
函数的链式访问。
char* my_strcpy (char* dest,const char* src)
{
assert(src != NULL);
assert(dest != NULL);
//记录目标空间的起始地址
char* ret = dest;
//实现了整个字符串的拷贝
while ((*dest++ = *src++)) {
;
}
//返回目标空间的起始地址
return ret;
}
char arr1[] = "xxxxxxxxxxxxx";
char arr2[] = "hello";
printf("%s\n",my_strcpy(arr1,arr2));//链式访问
二、strlen函数
目前已知有三种方法可以实现strlen函数:计数器版本(无assert和const),递归版本,指针-指针
1.创建临时变量(计数器版本)求解
思路:将数组传过去,从字符串的第一个字符开始判断,如果第一个字符不是\0,那么加一记录
一个字符(count++),并且将字符串向后挪一位(str++),如果检测到\0,结束循环,返回count,
即字符串长度。
int my_strlen(char* str)
{
int count = 0;
while(*str != '\0'){
count++;//记录字符个数
str++;//字符向前一位
}
return count;
}
2.利用assert和const 优化strlen函数
思路:为了防止有人传空指针,所以加上assert;strlen函数本质上是不会改变字符串内容的,所
以用const修饰从而保护字符串内容。这样做可以增加代码的健壮性(鲁棒性)。
int my_strlen (const char* str)
{
assert(str != NULL);
int count = 0;
while (*str != '\0') {
count++;
str++;
}
return count;
}
3.又发现字符串长度一定是一个整数,所以可以将返回值类型改为unsigned int (size_t)
size_t my_strlen (const char* str)
{
assert(str != NULL);
int count = 0;
while (*str != '\0') {
count++;
str++;
}
return count;
}
三、strcat函数
思路:先将dest指向‘\0’,在追加源字符串(相当于strcpy)
最后返回目标字符串的地址
char* my_strcat (char* dest ,const char* src)
{
char* ret = dest;
//断言
assert(dest && src);
//1.先找到dest的\0
while (*dest!='\0') {
dest++;
}
//2.追加源字符串,源字符串的全部(字符部分和\0)
while ((*dest++ = *src++)) {;}
return ret;
}
四、strcmp函数
思路:一个一个字符比,相等返回0
int my_strcmp (const char* str1 ,const char* str2)
{
assert(str1 && str2);
while (*str1==*str2)
{
if(*str1 == '\0')
{
return 0;
}
str1++;
str2++;
}
if(*str1>*str2)
return 1;
else
return -1;
}
int my_strcmp (const char* str1 ,const char* str2)
{
assert(str1 && str2);
while (*str1==*str2)
{
if(*str1 == '\0')
{
return 0;
}
str1++;
str2++;
}
return *str1-*str2;
}