前言:
通过C语言字符串函数的知识,这篇将对strncpy、strncat、strncmp函数进行深入学习底层原理的知识,并模拟实现对应功能。
/知识点汇总/
长度不受限制的字符串函数:strcat,strcmp,strcpy
介绍长度受限制字符串函数:strncat,strncmp,strncpy
字符串查找函数:strstr
函数原型:char* strncpy(char* strDest, const char* strSource, size_t count);
函数功能:完成源字符串到目标字符串的拷贝,返回目标字符串的起始地址,返回值类型为char*,另外还可指定拷贝的长度
头文件:
使用注意事项:
(1)、源字符串必须以’\0’结束(因为会包括’\0’一起拷贝过去)
(2)、拷贝会将源字符串中的’\0’拷贝到目标空间
(3)、目标空间必须足够大,确保能存放源字符串
(4)、目标空间必须可变(不能是常量)
(5)、初始化为数组形式时,空间需要指明合适的大小
示例代码1如下:
#include
#include
int main()
{
char arr1[20] = { 0 };
char arr2[] = "abcdefghi";
strncpy(arr1, arr2, 3);
printf("%s\n", arr1);//abc
char arr1[20] = "xxxxxxxxxxxxxxx";
char arr2[] = "abcdefghi";
strncpy(arr1, arr2, 3);
printf("%s\n", arr1);//abcxxxxxxxxxx
return 0;
}
示例代码2如下:
探讨拷贝长度的因素,当指定的拷贝长度比源字符串长度大时,自动补\0
#include
#include
int main()
{
char arr1[20] = "xxxxxxxxxxxx";
char arr2[] = "abc";
strncpy(arr1, arr2, 6);//6长度比str2长时,自动补的'\0'.
printf("%s\n", arr1);//abc\0\0\0
return 0;
}
#include
#include
char* my_strncpy(char* str1, const char* str2, size_t num)
{
assert(str1 && str2);
char* ret = str1;
while (num--)
{
*str1++ = *str2++;
}
return ret;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "defabc";
printf("请输入拷贝的字符串长度:>");
size_t len = 0;
scanf("%zd", &len);
printf("%s\n", my_strncpy(arr1, arr2,len));
return 0;
}
解释说明:
1.assert是断言,参数为指针,防止传参过来是空指针避免野指针的问题
2.用一个指针变量始终保存目标字符串的起始地址,以免目标起始地址发生改变,导致函数的返回值错误
3.num–执行的就是依次拷贝字符串的内容,直到num = 0,从而限制了拷贝个数,最后跳出while循环
函数原型:char* strncat(char* strDest, const char* strSource, size_t count);
函数功能:完成源字符串到目标字符串的追加,返回目标字符串的起始地址,返回值类型为char*,另外还可指定追加的长度
头文件:
使用注意事项:
(1)、源字符串必须以’\0’结束(因为会包括’\0’一起追加过去)
(2)、追加会将源字符串中的’\0’拷贝到目标空间
(3)、目标空间必须足够大,确保能存放源字符串
(4)、目标空间必须可变(不能是常量)
(5)、初始化为数组形式时,空间需要指明合适的大小
示例代码1如下:
#include
#include
int main()
{
char arr1[20] = "abc";
char arr2[] = "defghi";
strncat(arr1, arr2, 3);
printf("%s\n", arr1);//abcdef
return 0;
}
示例代码2如下:
探究’\0’的追加情况1
#include
#include
int main()
{
char arr1[20] = "abc\0xxxxxxxxxxx";//字符串中本身具备'\0'时,依然以'\0'开始覆盖,最后补'\0'结束
char arr2[] = "defghi";
strncat(arr1, arr2, 3);
printf("%s\n", arr1);//abcdef\0
return 0;
}
示例代码3如下:
探究’\0’的追加情况2
#include
#include
int main()
{
char arr1[20] = "abc\0xxxxxxxxxxx";//字符串中本身具备'\0'时,依然以'\0'开始覆盖,最后补'\0'结束
char arr2[] = "defghi";
strncat(arr1, arr2, 10);//10长度比str2长时,自动在末尾补'\0'.此函数就不会对超出长度的字符进行操作了
printf("%s\n", arr1);//abcdef
return 0;
}
小结:
①、目标字符串中本身具备’\0’时,依然以’\0’开始覆盖,最后补’\0’结束
②、指定长度长度比源字符串长时,自动在末尾补’\0’,且此函数就不会对超出长度的字符进行操作了
#include
#include
char* my_strncat(char* str1, const char* str2, size_t num)
{
assert(str1 && str2);
char* ret = str1;
while (*str1 != '\0')
{
str1++;
}
//*str1--;
while (num--)
{
*str1++ = *str2++;
}
*str1 = '\0';
return ret;
}
int main()
{
char arr1[10] = "abc";
char arr2[10] = "abc";
printf("请输入追加的字符串长度:>");
size_t len = 0;
scanf("%zd", &len);
printf("%s\n", my_strncat(arr1, arr2, len));
return 0;
}
解释说明:
1.assert是断言,参数为指针,防止传参过来是空指针避免野指针的问题
2.用一个指针变量始终保存目标字符串的起始地址,以免目标起始地址发生改变,导致函数的返回值错误
3.str1 != '\0’执行的就是指针遍历目标字符串的内容,直到目标字符串的下一个地址,从而再以num–限制追加的字符长度
4.值得注意的是str1 = ‘\0’,最后需要置结束标志位,否则就是乱码,因为直到遇见’\0’才结束。
函数原型:int strncmp( const char *string1, const char *string2, size_t count );
函数功能:字符串大小比较,返回值类型为int,另外还可指定比较的长度
头文件:
示例代码1如下:
#include
#include
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcqw";
int ret = strncmp(arr1, arr2, 3);
printf("%d\n", ret);//0
int ret2 = strncmp(arr1, arr2, 4);
printf("%d\n", ret2);//-1
return 0;
}
#include
#include
int my_strncmp(const char* str1,const char* str2, size_t num)
{
assert(str1 && str2);
while (num && *str1 && *str2)
{
if (*str1 > *str2)
{
return 1;
}
if (*str1 < *str2)
{
return -1;
}
num--;
str1++;
str2++;
}
return 0;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "defabc";
printf("请输入比较的字符串长度:>");
size_t len = 0;
scanf("%zd", &len);
printf("%d\n", my_strncmp(arr1, arr2,len));
return 0;
}
解释说明:
1.assert是断言,参数为指针,防止传参过来是空指针避免野指针的问题
2.用一个指针变量始终保存目标字符串的起始地址,以免目标起始地址发生改变,导致函数的返回值错误
3.num && *str1 && *str2执行的就是依次比较字符串的内容,直到源字符串/目标字符串/指定字符长度 = 0结束,从而限制了比较字符个数,最后跳出while循环
函数原型:const char *strstr( const char *string, const char *strCharSet );
函数功能:在字符串中找字符串(字符串中找子字符串或子段)
头文件:
返回值:strstr会返回主字符串中子字符串第一次出现的位置,如果主字符串中没有子字符串,则返回NULL
示例代码1如下:
#include
#include
int main()
{
char arr1[] = "abcdefghi";
char arr2[] = "def";
char* ret = strstr(arr1, arr2);
if (ret == NULL)
{
printf("找不到\n");
}
else
{
printf("%s\n", ret);//defghi
}
return 0;
}
蛮力法,直接遍历查找
#include
#include
const char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
const char* cp;//记录开始匹配的位置
const char* s1;//遍历str1指向的字符串
const char* s2;//遍历str2指向的字符串
if (*str2 == '\0')
return str1;
cp = str1;
while (*cp)
{
s1 = cp;
s2 = str2;
while (*s1 && *s2 && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
return cp;
cp++;
}
return NULL;
}
int main()
{
char arr1[] = "abcdefghi";
char arr2[] = "def";
const char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
{
printf("找不到\n");
}
else
{
printf("%s\n", ret);//defghi
}
return 0;
}
解释说明:
1.assert是断言,参数为指针,防止传参过来是空指针避免野指针的问题
2.定义三个指针变量,cp用于标记返回的起始地址,s1和s2用于遍历str1和str2逐个比较即可,
3.*s1 && *s2 && *s1 == *s2执行的就是依次比较字符串的内容,直到子字符串与主字符串内容匹配则结束,最后跳出while循环,返回cp的地址。
掌握模拟函数的逻辑思维尽可能考虑全面,学会利用画逻辑图分析并一步步的推理
学习函数的最实用的方式就是用自己的逻辑简单实现一些类似的功能
半亩方糖一鉴开,天光云影共徘徊。
问渠哪得清如许?为有源头活水来。–朱熹(观书有感)