前言:
掌握常用的字符串函数,会有很多好处。
可以更方便地处理字符串操作,大大提高代码的可读性和可维护性,避免自己编写复杂的字符串处理逻辑,从而减少错误的可能性。另外,一些字符串函数还可以提高程序的运行效率,比如使用 strstr 函数来查找字符串中的子串,比自己遍历字符串要快得多。
总之,掌握字符串函数可以让C 语言编程更加高效、准确。
那么本篇文章围绕常用的字符串函数进行运用及模拟实现,帮大家深入理解这些函数,以便更灵活地运用。
1.strlen的使用和模拟实现
-
函数声明
size_t strlen ( const char * str );
可以看到,strlen的返回值为 size_t(简单理解为 unsigned int,即无符号整型),函数参数为
const char * str
-
使用方法
1.strlen的使⽤需要包含头文件
#include <stdio.h>
2.字符串以 '\0' 作为结束标志
strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' ) 。如果字符串中没有'\0', strlen函数也会超出字符串外去查找,直到遇到'\0'才会停止。
那么参数指向的字符串必须要以 '\0' 结束。否则就计算不出字符串正确的长度
我们以下面的代码为例
#include <stdio.h>
#include <string.h>
int main()
{
const char* str1 = "abcd";//结尾有'\0'
char str2[] = {'a','b','c','d'};//结尾无'\0'
printf("%zd\n", strlen(str1));
printf("%zd\n", strlen(str2));
return 0;
}
输出结果为
而且vs会有警告
3.注意函数的返回值为 size_t,是无符号的。
这里容易掉坑里,我们来看个例子:
#include <stdio.h>
#include <string.h>
int main()
{
const char* str1 = "abcdef";
const char* str2 = "abcd";
if (strlen(str2) - strlen(str1) > 0)
{
printf("str2>str1\n");
}
else
{
printf("srt1>str2\n");
}
return 0;
}
结果是:
str2="abcd", str1="abcdef", 那么str2的长度为4,str1长度为6
但 strlen(str2) - strlen(str1) > 0
例题解析:
1.我们知道,有符号整数在二进制表示中,有符号位和数值位两部分,最高位的1位是被当作符号位,剩余的都是数值位。符号位用0表示“正”,用1表示“负”。(下图为-1和1的二进制原码表示形式。)
而在无符号数中,最高位不是符号位,就是它的数值位。所以下图中的int i=-1的二进制码值,如果表示的是unsigned int,那就是相当大的数字了!
2.计算机内整型类的数据以二进制的补码形式进行存储,我们输入与计算机为我们输出的整数是原码,它们有这样的关系:
需要注意,无符号整数和正整数的原码、反码和补码是相同的,不进行上图的转换计算。
3.解这道题的关键,就在于需要注意,strlen函数返回值为 size_t,是无符号的,那么strlen(str2) - strlen(str1)的结果也是无符号的。strlen(str2) - strlen(str1)=4-6=-2,负二作为无符号的数字被表示出来会是一个相当大的数。
-
模拟实现
1.计数器方式
思路:遇到‘\0’停止,没遇到就让计数器+1
int my_strlen(const char* str)
{
int count = 0;
assert(str);
while(*str) //*str为'\0'时停止循环
{
count++;
str++;
}
return count;
}
2.指针-指针方式
思路:两个指针相减得到的是它们之间的元素个数(就像日期和日期相减得到天数一样),让'\0'对应的指针减去第一个字符对应的指针就能得到整个字符串长度。
int my_strlen(const char* str)
{
assert(str);
char *p = str;
while(*p)
p++;
return p-s;
}
3.递归
如果题目有要求,不能创建临时变量,那么我们就要用递归。
int my_strlen(const char* str)
{
assert(str);
if(*str == '\0')
return 0;
else
return 1 + my_strlen(str+1);
}
2.strcpy的使用和模拟实现
-
函数声明
char* strcpy(char* destination, const char* source);
strcpy函数会将source指向的字符串拷贝到destination指向的字符串,并返回destination指向字符串的首地址。
需要包含头文件
#include <string.h>
-
使用方法
1.源字符串必须以‘\0’结束,会将源字符串的'\0'也拷贝到目标空间。
2.目标空间必须足够大,能够存放下源字符串。
3.目标空间必须可修改。
这里也容易出现小错误,我们看一个例子。
#include <stdio.h>
#include <string.h>
int main()
{
char* dest = "xxxxxxxxxxxxxxx";//常量字符串,不可更改!!
char* src = "i love you!";
strcpy(dest, src);
return 0;
}
这样写就不会有输出,因为dest是常量字符串,不能够被修改!
正确写法:
#include <stdio.h>
#include <string.h>
int main()
{
char dest[] = "xxxxxxxxxxxxxxx";//写成数组的形式就可以被修改了
char* src = "i love you!";
strcpy(dest, src);
printf(dest);
return 0;
}
结果是:(因为"i love you!"后面有‘\0’,strcpy把‘\0’,也给复制到了dest里,所以dest后面多的几个xx就不会被打印出来)
-
模拟实现
思路:过程很简单,就是复制过去一个,指针指向下一个字符,直到source指向‘\0’。如下图
用代码来实现一下
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;//用ret存起始地址,之后方便return
while((*dest++=*src++)) //两边先使用,后++
{
;
}
return ret;
}
根据优先级顺序,中间的while部分的实现过程是这样的:
while(*src!='\0')
{
*dest=*src;
dest++;
src++;
}
但是写成上面的形式会更加巧妙简洁。
3.strcat的使用和模拟实现
-
函数声明
char * strcat ( char * destination, const char * source );
strcat和strcpy很像,只是前者是在destination指向的字符串后追加source指向的字符串,而后者直接将source指向字符串复制到destination指向的字符串中。
需要包含头文件
#include <string.h>
-
使用方法
1.源字符串必须以‘\0’结束,会将源字符串的'\0'也拷贝到目标空间。
2.目标字符串必须以'\0'结束,否则没办法知道从哪里开始追加。
3.目标空间必须足够大,能够存放下源字符串。
4.目标空间必须可修改。
举个栗子:
#include <stdio.h>
#include <string.h>
int main()
{
char dest[20] = "xxx ";
char* src = "i love you!";
strcat(dest, src);
printf(dest);
return 0;
}
运行结果:
-
模拟实现
思路:和strcpy函数差不多,只不过这次要先找到destination字符串的‘\0’,从‘\0’开始追加source字符串,所以只需要先找到‘\0’,后面的过程和strcpy就是一样的了。
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;//存dest的首地址
assert(dest && src);
while (*dest)//让dest指向'\0’,从'\0'开始连接字符串
dest++;
while ((*dest++ = *src++))
;
return ret;//返回目标字符串的首地址
}
!需要注意:strcat是可以实现字符串自己给自己追加的,但我们写的my_strcat是没办法实现的!
4.strcmp的使用和模拟实现
-
函数声明
int strcmp ( const char * str1, const char * str2 );
通过依次比较字符的ASCII码值来比较两个字符串大小。
str1 > str2 返回大于0的数字; str1 = str2 返回0; str1 < str2 返回小于0的数字。
需要使用头文件:
#include <string.h>
-
函数使用
1.两个字符串比较大小,不能够直接用大于号或小于号来比较,必须要用strcmp函数。
2.只要有一个字符能够比出大小就结束。
举个栗子:
#include <stdio.h>
#include <string.h>
int main()
{
printf("%d\n", strcmp("abcd", "abcd")); //0
printf("%d\n", strcmp("abcd", "abc")); //1
printf("%d\n", strcmp("abcd", "abcde"));//-1
printf("%d\n", strcmp("abcd", "acd")); //-1
return;
}
结果:
在vs里,返回的大于0的数就是1,小于0的数是-1。
-
模拟实现
思路:从字符串首地址开始比较,字符相同,两个字符串就继续比较下一个字符;
字符不同,就比较大小,判断返回值比0大还是比0小;
当两个字符串都同时比较到了'\0',说明两个字符串大小相同。
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)//字符相等,进入循环,继续比较下一个
{
if (*str1 == '\0')//因为两个字符是相等的,一个是'\0'那两个都是'\0'
return 0; //说明两个字符都同时比较到了字符串结尾,字符串相等
str1++;
str2++;
}
return *str1 - *str2;//如果*str1>*str2,返回正数,反之
}
这样写最后的返回值是*str1和*str2的ASCII码值的差值,就不一定是-1或1了,如果想要是-1或1,需要用if再判断一下。
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)//字符相等,进入循环,继续比较下一个
{
if (*str1 == '\0')//因为两个字符是相等的,一个是'\0'那两个都是'\0'
return 0; //说明两个字符都同时比较到了字符串结尾,字符串相等
str1++;
str2++;
}
if(*str1 > *str2)
return 1;
if(*str1 < *str2)
return -1;
}
5.strstr的使用和模拟实现
-
函数声明
char * strstr ( const char * str1, const char * str2 );
需要包含头文件
#include <string.h>
-
函数使用
1.函数返回字符串str2在str1中第一次出现的位置。
2.找不到返回NULL。
3.不比较'\0',以'\0'作为结束标志。
举个例子:
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "This is a simple string";
char* pch;
pch = strstr(str, "simple");
printf("%s\n", pch);
return 0;
}
结果是:
-
模拟实现
思路:必然需要一个字符一个字符地查找,那么会出现下面三种情况:
第一种情况,查找到str1的'\0'字符还没有找到,就结束查找,找不到需要返回NULL;
第二种情况,查找到第一个相同的字符之后,判断后续字符是否相等,相等了返回第一次出现的位置,所以我们需要一个指针来存str1中第一次出现与str2相同字符位置的地址,在这里就是str1的'b'字符。
第三种情况,第一个字符相同,但第二个字符不同,需重新查找,所以str2的地址不能改变,需要再用一个指针来进行加减。
综上,为了模拟实现strstr函数,除了str1指针和str2指针外,我们还需要一个指针存储字符串str2在str1中第一次出现的位置【char* cp】,一个指针来对str2指向的字符串进行加减【char* s2】,也可以再加一个指针来对str1指向的字符串进行加减【char* s1】。
代码:
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 != NULL);
assert(str2 != NULL);
char* cp = (char*)str1;//str1是const char* 类型,需要强制转换
char* s1 = NULL;
char* s2 = NULL;
if (!*str2)//当str2是空字符'\0'时,返回str1的地址
return (char*)str1;
while (*cp) //当cp指向'\0',就是找到了str1的结尾都没有找到,跳出循环,返回NULL
{
s1 = cp;
s2 = (char*)str2;
while (*s1 && *s2 && (*s1 == *s2)) {//s1和s2都不能是'\0'
s1++; //s1和s2相同,就继续向后查找,
s2++;
}
if (!*s2)//s2是'\0'时意味着已经在str1里找到了str2字符串
return cp;
cp++;
}
return (NULL);
}
6.strtok函数的使用
-
函数声明
char * strtok ( char * str, const char * delimiters );//delimiter 分隔符
通俗的讲,这个函数用来去掉字符串里指定的分隔符。
-
使用方法
1.delimiters参数指向⼀个字符串,定义了⽤作分隔符的字符集合。
2.str指定⼀个字符串,它包含了0个或者多个由delimiters字符串中⼀个或者多个分隔符分割的标
![](https://i-blog.csdnimg.cn/blog_migrate/6bc0c6d610293cfbfd9c120660f971ba.png)
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "iamyu@qq.com";
char str2[20] = { 0 };
strcpy(str2, str1);//拷贝一份使用
char* ret = NULL;
ret = strtok(str2, "@.");
printf("%s", ret);
return 0;
}
结果:
4.在上面的例子中,函数第一个参数即str不为NULL,说明是第一次在此字符串里查找分隔符,函数会找到第一个分隔符在的位置,将它改为'\0'后还会记住它的位置(如上面的例子)。
想要函数继续查找,第一个参数必须为NULL。
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "iamyu@qq.com";
char str2[20] = { 0 };
strcpy(str2, str1);
char* ret = NULL;
//第一次查找
ret = strtok(str2, "@.");
printf("%s\n", ret);
//第二次查找
ret = strtok(NULL, "@.");
printf("%s\n", ret);
return 0;
}
我们在第二次查找时,给函数的第一个参数改为NULL,它继而就能找到被分隔的第二个字符串"qq"。看一下代码执行结果:
以此类推,想要输出"com"这个字符串,我们需要第三次查找!
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "iamyu@qq.com";
char str2[20] = { 0 };
strcpy(str2, str1);
char* ret = NULL;
//第一次查找
ret = strtok(str2, "@.");
printf("%s\n", ret);
//第二次查找
ret = strtok(NULL, "@.");
printf("%s\n", ret);
//第三次查找
ret = strtok(NULL, "@.");
printf("%s\n", ret);
return 0;
}
结果:
5.至此,我们已经查找完毕,但是如果载查找一次呢?函数就会返回NULL指针。
最终代码:上面的代码过于啰嗦,再来简化一下
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "iamyu@qq.com";
char str2[20] = { 0 };
strcpy(str2, str1);//拷贝一下使用
char* ret = NULL;
for (ret = strtok(str1, "@."); ret != NULL; ret = strtok(NULL, "@.")) {
printf("%s\n", ret);
}
return 0;
}
结果:
解释一下for循环的意思:
7.strncpy,strncat,strncmp函数的使用
这三个函数我就一起说了
-
strncpy的使用
函数声明
char * strncpy ( char * destination, const char * source, size_t num );
1.strcpy是把源字符串整体都拷贝到目标空间中,那strncpy就是只拷贝源字符串中num个字符到目标空间。
2.如果源字符串比num长,不会在目标后再去追加'\0'。
3.如果源字符串长度小于num,拷贝完字符串后,在目标后面追加'\0',直到num个。
-
strncat的使用
函数声明
char * strncat ( char * destination, const char * source, size_t num );
1.strncat只追加num个源字符串中的字符到目标空间,并且会在后面再追加'\0'字符。
2.如果源字符串长度小于num,只会追加到源字符串的'\0',不会再向后补'\0'。
-
strcmp
函数声明
int strncmp ( const char * str1, const char * str2, size_t num );
strncmp最多比较str1和str2中num个字符,比较规则和strcmp是相同的,这里不多赘述。