上篇博客是求字符串长度的函数,这篇把其他几个常用的字符串函数也都介绍一下,并且模拟一下,如下:
1. strcpy 函数是拷贝字符串函数的,注意的点:
a. strcpy 被拷贝的字符串必须以 \0 结尾,也会把 \0 也拷贝过去。
b. 拷贝目标的空间要足够大(虽然小于拷贝的长度 也能拷贝过去 但是是错误的)
c. 拷贝的目标要可以可变
strcpy 函数返回的是目标空间的起始地址
strcpy 函数的返回类型的设置是为了实现链式访问
简单使用一下 strcpy 函数,如下:
#include <stdio.h>
#include <string.h>
int main()
{
// char arr1[10] = { 0 };
// char arr2[] = "zxcv"; // 后面默认有 \0
// char arr2[] = { 'z', 'x', 'c', 'v' }; // 后面没有 \0 程序会挂掉
// char arr2[] = { 'z', 'x', 'c', 'v', '\0' }; // 可以自己加上 \0
// char arr2[10] = { 'z', 'x', 'c', 'v' }; // 或者初始化数组大小 后面默认初始化为 0
// char* arr1[] = "cvbzxcvbxcvb"; // 常量字符串 不能改变
char arr1[20] = "XXXXXXXXX"; // 可以查看 \0 是否拷贝过去
char arr2[] = "zxcvb";
// strcpy(arr1, arr2);
// printf("%s\n", arr1);
printf("%s\n", strcpy(arr1, arr2));
return 0;
}
2. strcmp 函数的字符串比较函数,比较字符串大小的,注意的点:
a. 返回值:
第 1 个字符串大于第 2 个字符串返回大于 0
第 1 个字符串小于第 2 个字符串返回小于 0
第 1 个字符串等于第 2 个字符串返回等于 0
b. strcmp 函数比较的不是字符串长度 !!! 而是比较字符串中对应位置上的字符的大小,如果相同就比较下一对儿,直到不同或者都遇到 \0
简单使用一下 strcmp 函数,如下:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "zxcvbnm";
char arr2[] = "zxavbnm";
int ret = strcmp(arr1, arr2);
if(ret > 0)
{
printf(">\n");
}
else if(ret == 0)
{
printf("=\n");
}
else
{
printf("<\n");
}
printf("%d\n", ret);
return 0;
}
3. strcat 函数是追加字符串函数,注意的点:
a. 源字符串必须以 \0 结束
b. 目标空间必须可以修改
那么自己给自己追加呢?
要被追加的字符串的 \0 被覆盖之后 , 等于说要追加的字符串的 \0 也没有了 \0 所以程序会挂掉(死循环)
简单使用一下 strcat 函数,如下:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "asdfg"; // 后面有 \0
char arr2[] = "vbnmsdf"; // 从 \0 处开始追加(g 后的\0被覆盖,但是f后 \0 也追加过去了)
char arr3[20] = "hello\0XXXXXXX"; // 后面有 \0
char arr4[] = " bit"; // b 前面的空格覆盖 \0 ;并且带上 t 后面的 \0 追加上去了
// char* temp = strcat(arr1, arr2);
// printf("%s\n", temp);
printf("%s\n", strcat(arr1, arr2));
printf("%s\n", strcat(arr4, arr4));
return 0;
}
4. strstr 函数是判断一个字符串是否是另一个字符串的子串:
简单使用一下 strstr 函数,如下:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "asdfghjkl";
char arr2[] = "dfgh";
char* ret = strstr(arr1, arr2);
if(NULL == ret)
{
printf("找不到子串\n");
}
else
{
printf("%s\n", ret);
}
return 0;
}
以上就是字符出函数的使用,紧接着下面我会把上面的函数都模拟实现一下。
1. 模拟 strcpy 函数,如下:
因为 strcpy 返回的是 char* 类型的函数,所以我们可以创建 char* 类型的指针变量接收:
char* ret = My_strcpy(arr1, arr2);
printf("%s\n", ret);
但正是因为返回值是 char* 类型的,所以我们可以不用创建变量,用链式访问来接收(我模拟实现也是用链式访问):
printf("%s\n", My_strcpy(arr1, arr2)); // 链式访问
实现拷贝部分,可以这样写:
缺点就是 while 循环条件里面,当 *source 为 '\0' 的时候,条件为假,退出循环,'\0' 没有在里面拷贝,需要在外面单独拷贝一下 '\0'
char* My_strcpy(char* dest, const char* source)
{
assert(dest && source);
char* temp = dest;
while(*source != '\0')
{
*dest = *source;
dest++;
source++;
}
*dest = *source; // 单独拷贝 '\0'
return temp;
}
所以我改了最优版:
while 循环里面是赋值语句,用后置++ ,当把 '\0' 赋值过去的时候,已经拷贝了过去了,但是循环为假,然后退出循环
char* My_strcpy(char* dest, const char* source)
{
assert(dest && source); // 断言 判断指针是否为空
char* temp = dest;
while(*dest++ = *source++)
{
;
}
return temp;
}
全部代码如下:
#include <stdio.h>
#include <string.h>
#include <assert.h>
// strcpy 函数返回的是目标空间的起始地址
// strcpy 函数的返回类型的设置是为了实现链式访问
char* My_strcpy(char* dest, const char* source)
{
assert(dest && source); // 断言 判断指针是否为空
char* temp = dest;
while(*dest++ = *source++) // 用后置++ ,当等于 '\0' 的时候,已经拷贝了 然后退出循环
{
;
}
return temp;
}
int main()
{
char arr1[20] = { 0 };
char arr2[] = "Hello world";
// char* ret = My_strcpy(arr1, arr2);
// printf("%s\n", ret);
printf("%s\n", My_strcpy(arr1, arr2)); // 链式访问
return 0;
}
2. 模拟 strcmp 函数,如下:
判断字符不相等部分,可以用 if 语句:
缺点:可以用 if 判断, 但缺点是 strcmp 函数比较字符串不相等的情况下返回是 >0 或者 <0 ,而不是 1 或者 -1 ,只是有的编译器会返回 1 或者 -1
if(*str1 > *str2)
{
return 1;
}
if(*str1 <*str2)
{
return -1;
}
所以我直接不相等的情况下,我把两个字符串做差,返回的是字符差值:
return (*str1 - *str2);
全部代码如下:
#include <stdio.h>
#include <string.h>
#include <assert.h>
int My_strcmp(const char*str1, const char* str2)
{
assert(str1 && str2); // 断言 判断指针是否为空
while(*str1 == *str2) // 当字符相等就++
{
if(*str1 == '\0') // 但是相等要判断是否是 '\0' ,是 '\0' 那么就相等,返回 0
{
return 0;
}
str1++;
str2++;
}
return (*str1 - *str2); // 不相等的可以用一个减去另一个 返回的是字符差值
}
int main()
{
char arr1[] = "zxmvbnm";
char arr2[] = "zxavbnm";
int ret = My_strcmp(arr1, arr2);
if(ret > 0)
{
printf(">\n");
}
else if(ret < 0)
{
printf("<\n");
}
else
{
printf("=\n");
}
printf("%d\n", ret);
return 0;
}
3. 模拟 strcat 函数,如下:
strcat 函数是字符串追加的,所以只要找到目标字符串的末尾,拷贝过去就好了(拷贝与strcpy 函数实现一样),拷贝与返回部分与 strcpy 函数一样,就不解释了,那么找到末尾就是找 '\0' 用 while 循环:
// 找目标函数 \0
while(*dest != '\0')
{
dest++;
}
全部代码如下:
#include <stdio.h>
#include <assert.h>
char* My_strcat(char* dest, const char* source)
{
assert(dest && source); // 断言 判断指针是否为空
char* temp = dest;
// 找目标函数 \0
while(*dest != '\0')
{
dest++;
}
// 拷贝
while(*dest++ = *source++)
{
;
}
return temp;
}
int main()
{
char arr1[20] = "Hello";
char arr2[] = " world";
// char* ret = My_strcat(arr1, arr2);
// printf("%s\n", ret);
printf("%s\n", My_strcat(arr1, arr2)); // 链式访问
return 0;
}
4. 模拟 strstr 函数,如下:
strstr 函数是比较一个字符串是否是另一个字符串的子串:
那么当我判断前面都不相等的情况下,走到第一个 c 的位置都相等,都往后走一下,走到下面这个位置的时候,又不相等,那么上面的字符串应该回到刚才匹配相等位置的下一个重新开始匹配,而下面的字符串应该回到起始位置重新开始匹配:
所以我创建两个指针 S1、S2 分别接收形参指针 str1、str2 ,我用创建的指针往后走匹配;再单独创建一个指针 start 记录匹配相同情况下的位置:
const char* S1 = str1;
const char* S2 = str2;
const char* start = str1;
如果 S1、S2 相等那么两个指针继续往下走,如果不相等,因为 start 是记录相同的位置,所以 start 初始值被赋 str1 ,从上面的字符串起始位置往后,如果两个字符串相同,那么此时 start 记录的就是相同的位置,如果不相同,那么不进入 while 循环, start 会往下走;然后 S1 就被赋 start 新位置,重新开始找;最后 if 接收 *S2 是否是 '\0' 的情况,如果是 '\0' 那么就相等:
S2 = str2;
S1 = start;
while( *S2 && *S1 && (*S1 == *S2) )
{
S1++;
S2++;
}
if(*S2 == '\0')
{
return (char*)start;
}
start++;
全部代码如下:
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* My_strstr(const char* str1, const char* str2)
{
assert(str1 && str2); // 断言 判断指针是否为空
const char* S1 = str1;
const char* S2 = str2;
const char* start = str1;
while(*start != '\0')
{
S2 = str2;
S1 = start;
while( *S2 && *S1 && (*S1 == *S2) )
{
S1++;
S2++;
}
if(*S2 == '\0')
{
return (char*)start;
}
start++;
}
return NULL;
}
int main()
{
char arr1[] = "zxccvbn";
char arr2[] = "cvb";
char* ret = My_strstr(arr1, arr2);
if(NULL == ret)
{
printf("没找到子串\n");
}
else
{
printf("%s\n", ret);
}
return 0;
}