![](https://img-blog.csdnimg.cn/direct/624521199ba2498a94e66670fe4562e2.png)
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcqdefg";
int ret = strcmp(arr1, arr2);
printf("%d\n", ret);//-1
//因为第4个位置上q的ascii值大于d的,所以字符串2大于1
return 0;
}
结果返回值是-1,但不一定,不同的编译器上对此的处理不同(编者这里使用的是vs2022),具体参考如下:
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
int ret = 0;
assert(str1 && str2);
while (*str1 == *str2)
{
if (*str1 == '\0')//此时两个字符串直至\0前都相等,说明两字符串相等,返回0
return 0;
str1++;
str2++;
}
return *str1 - *str2;//满足标准返回值的要求
}
应用:由strcmp的功能不难发现,它的功能可以应用于登录过程中的数据处理,即当用户向应用中输入账号密码后,通过strcmp函数就可以将用户输入值与原先数据库中的值进行比对。
之前我们学习过了一下字符串函数//strcpy
//strcat
//strcmp
但是它们都是长度不受限制的字符串函数,是不安全的,因为它们不会处理字符串的长度,存在越界访问的风险;因而在vs中使用会弹出报错提示,类似scanf要使用scanf_s,所以平时我们需要在代码前加上一个宏定义能让我们自由使用这些函数。
#define _CRT_SECURE_NO_WARNINGS 1
![](https://img-blog.csdnimg.cn/direct/b6e457c710664c26b4099fb58a15a3e4.png)
//strncpy
//strncat
//strncmp
//它们都是长度受限制的字符串函数,并且多了一个参数size_t num,是相对安全的,但也仅限于相对安全,同样需要在代码前加一个宏定义。
七.strncpy函数
作用功能与strcpy函数类似,但是多了一个参数num,即在原先strcpy函数功能基础上将字符串前num个字符进行处理。使用的时候同样要包含头文件<string.h>
拷⻉num个字符从源字符串到⽬标空间。 如果源字符串的⻓度⼩于num,则拷⻉完源字符串之后,在⽬标的后边追加\0,直到num个。
举个例子:
int main()//int main()
{
// char arr1[20] = "abcdef";
// char arr2[20] = "abqdefgh";
// int ret = strncmp(arr1, arr2, 6);
// //6表示最多比较6个
// printf("%s\n", arr2);
char arr1[20] = "abcdef";
char arr2[20] = "xxxxxxxxxxxx";
strncpy(arr2, arr1, 8);
//有几个拷贝几个
//如果多了的话用\0补足
printf("%s\n", arr2);//abcdef
return 0;
}
显然字符串arr1是不足8位的,但是strncpy函数会自动给arr1末位补上\0.
虽然如此,但是程序员写平时写代码的时候不会如此鲁莽,一般会事先计算好字符串大小再传参。
例如:
int main()
{
char arr1[20] = "abcdef";
char arr2[20] = "xxxxxx";
int len = strlen(arr1);
strncat(arr2, arr1, len);
printf("%s\n", arr2);
return 0;
}
八.strncat函数的使用
与上述函数类似,话不多说,上函数结构:
意思是:将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加⼀个 \0 字符。如果source 指向的字符串的⻓度⼩于num的时候,只会将字符串中到 \0 的内容追加到destination指向的字符串末尾。
例如:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[20]={0};
char str2[20]={0};
strcpy(str1, "To be ");
strcpy(str2, "or not to be");
strncat(str1, str2, 6);
printf("%s\n", str1);//To be or not
return 0;
}
九.strncmp函数的使用
函数机制与上述函数类似,这里不再赘述。
十.strstr的使用和模拟实现
注意:包含头文件<string.h>
功能:在一个字符串中查找另一个字符串,并记录匹配第一次的位置
函数结构:
由此可见,函数返回字符串str2在字符串str1中第一次出现的位置。字符串的比较匹配不包含\0字符,以\0作为结束标志。
上代码:
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="This is a simple string";
char * p;
p = strstr (str,"simple");//p指向了simple的首字符
strncpy (p,"sample",6);//通过p将simple修改为sample
printf("%s\n", str);//This is a sample string
return 0;
}
strstr函数的模拟实现:
strstr函数的模拟实现有几处难点:那就是情况多样,可能出现多次匹配。
对于它的模拟实现一般有两种方法:1.简单的写法-暴力查找。2.KMP算法-更加高效,实现和理解也更复杂。这里编者将着重讲解法1,对法2感兴趣的同学可以自行了解实现。
char* my_strstr(const char* str1, const char* str2)//在arr1中查找arr2
{
const char* s1 = NULL;
const char* s2 = NULL;
const char* cur = str1;//current中存放的是最终的返回值,即要从此处向后打印
//特殊场景处理,若str2是一个空字符串,则直接返回str1
if (*str2=='\0')
return (char*)str1;
while (*cur)
{
s1 = cur;
s2 = str2;
//while (*s1 == *s2)//有可能中途遇见\0了
while (*s1 !='\0' && *s2!='\0' && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return (char*)cur;
}
cur++;
}
//最终cur遍历完的时候都找不到的时候,返回空指针
return NULL;
}
int main()
{
char arr1[] = "this is an apple";
//const char* p = "is"; 只要是字符串地址就行
char arr2[] = "is";
char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
{
printf("找不到!\n");
}
else
{
printf("%s\n", ret);//is is an apple
}
return 0;
一般的情况是好理解的,这里不多解释,主要解释一下特殊情况:就是提前匹配到目标字符串的前几个字符,但是不完全匹配。这里画图给读者们简要解释一下,目标字符串bbc在源字符串中可能会先匹配到前两个bb但是到了第三个b与与bbc就不匹配了,这里就需要重置指针,所以在模拟实现中我们要设置两个新指针s1,s2,而尽量不适用str1,str2,这是为了字符串的起始位置不被破坏,方便查找,而cur的作用是为了更新str1的查找位置,一点一点向后挪动。
十一.strtok函数的使用
很多同学第一次见到这个函数的时候可能不是很好理解这个函数,别担心。
char * strtok ( char * str, const char * sep);
int main()
{
char arr1[] = "zsanfeng@yeah.net@hehe";
char arr2[30] = { 0 };
strcpy(arr2, arr1);
const char* sep = "@.";
char* ret = NULL;
for (ret = strtok(arr2, sep);ret != NULL;ret = strtok(NULL, sep))
{
printf("%s\n", ret);
// zsanfeng
// yeah
// net
// hehe
}
/*ret = strtok(arr2, sep);
printf("%s\n", ret);
ret = strtok(NULL, sep);
printf("%s\n", ret);
ret = strtok(NULL, sep);
printf("%s\n", ret);*/
return 0;
}
这里的循环结构非常巧妙,望读者细细体会。
十二.strerror函数
注意:要包含头文件<errno.h>
![](https://img-blog.csdnimg.cn/direct/81d033dc039e40a49fd03b4a24fc4e0f.png)
#include <errno.h>
#include <string.h>
#include <stdio.h>
//打印⼀下0~10错误码对应的信息
int main()
{
int i = 0;
for (i = 0; i <= 10; i++)
{
printf("%s\n", strerror(i));
}
return 0;
}
有什么用呢,这里为大家举一个示例:
int main()
{
//fopen以读的形式打开文件的时候,如果文件不存在,就打开失败了
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));//将错误码翻译为错误信息
return 1;
}
//读文件
//关闭文件
fclose(pf);
return 0;
}
上文说过,strerror只能翻译错误信息后记录下来,不能直接打印,需要借助Printf函数。
这里再为大家介绍一个将二者功能合二为一的函数perror(printf+strerror),
上代码:
int main()
{
//fopen以读的形式打开文件的时候,如果文件不存在,就打开失败了
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("zhangsan");
//有能力直接打印错误信息,打印的时候,先打印传给perror的字符穿,然后打印冒号,再打印空格,最后打印错误码对应的错误信息
return 1;
}
//读文件
//关闭文件
fclose(pf);
return 0;
}