前言
在平常使用中对于字符串的操作是比较多的,经常会用到字符串大小的比较、拷贝甚至是查找以及各种类型数据的拷贝。熟练运用并掌握底层实现原理,有助我们拓宽思维,增强对语言的理解。
本文讲从介绍函数的使用以及详细的底层实现原理,来帮读者加深字符串函数的运用。
一、常见字符串函数
在C语言中,字符串规定为常量,表示不可对其修改,字符串变量是其首元素地址。
字符串函数都必须包含string.h头文件
1.strcpy
有字符串str1和字符串str2,如果我们想将字符串str1拷贝到str2中(假设str2空间足够大),我们无法使用简单的“="赋值操作符号。这将会是一个很麻烦的事情,库函数strcpy就实现这个繁琐操作。
先来看一下函数的接口
该函数有俩个参数,destination目的地和source源头,返回类型是char类型的指针
想将str1拷贝到str2中,只需要将str2传入第一个实参,str1传入第二个实参,用一个char类型指针接收结果。
注意:
- 由于strcpy拷贝字符串,所以源头的字符串中必须要有'\0',编译器才能知道要拷贝到哪里停下
- 目标空间要足够大(在vs编译器上,虽然能得出结果,但是程序会奔溃)
模拟实现strcpy
实现思想简单,俩个指针,一个循环,每次拷贝一个字节。拷贝完双指针各种一步
//模拟实现strcpy
char* my_strcpy(char* des, const char* sour)
{
assert(des && sour);
char* cur = des;
while (*cur++ = *sour++)
{
;
}
return des;
}
2.strcat
字符串拷贝函数,会覆盖原有的字符串,strcat函数则是在目标字符串末尾追加源字符串
同样,函数接口与strcpy一致
简单的举一个例子
注意:
- strcat追加函数的目标字符串和源字符串都必须有'\0',目标字符串的'\0'告诉编译器拷贝到哪里,源字符串的'\0'告知编译器拷贝哪里结束
- 目标空间必须足够大(同样的能拷贝,但是程序会奔溃)
- 不能自己给自己追加(自己给自己追加时,末尾的'\0'会被覆盖,程序会崩溃)
模拟实现:
双指针,一个指针先指向sour的起始位置,一指针指向des的起始,des先走,直到找到'\0',
之后二者一起走
模拟实现strcat
char* my_strcat(char* des, const char* sour)
{
assert(des && sour);
char* head = des; //保存头指针
while (*des++)
{
;
}
des--;
while (*des++ = *sour++)
{
;
}
return head;
}
3.strcmp
比较字符串大小的函数
模拟实现
//模拟实现strcmp
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
char* s1 = str1;
char* s2 = str2;
while (*s1 == *s2)
{
s1++;
s2++;
if (*s1 == '\0')
{
return 0;
}
}
return *s1 - *s2;
}
4.strtok
在一些特定的时候,要对字符串的内容进行分割。比如ip地址 192.168.10.1.1
要分割成四部分 192 168 10 1 1,自己实现就比较麻烦,strtok函数就可以轻松实现。
- strtok的第一个参数是需要分割的字符串,第二个参数是分割符
- strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。
- strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
- strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
- strtok会改变要分割的字符串,所以经常用一份字符串的拷贝来分割
strtok的用法是比较麻烦,下面就举个例子,来加深对strtok的用法
通过strtok得到第一个分割符前的字符串abc;
由于第一个分割符号的位置已经被保存,所以第二次分割时,strtok的第一个参数因为空指针
顺利分割出def,类似的在进行一次分割,就能得到字符串csdn
但是这样写,代码显得过于冗余,要我们下面将介绍一个巧妙的写法
进入循环,第一次执行strtok,p被赋值为abc首元素地址,打印字符串,执行strtok(NULL,".,"),p被赋值为def的首元素地址
进入第二次循环,检查p是否为空,不为空打印字符串def。
依次打印,直到p为空。
二、进阶版本
1.带个数函数的使用
strcpy、strcmp、strcat默认跟进全部元素,在一定意义上,限制了函数的灵活,所以库函数提供更高的版本.strncpy,strcmp,strncat 就可以指定拷贝、比较、追加的字节个数。
用法:在函数第三个参数传入要比较的字节数
strncpy
strncat
strncmp
用法与上面俩种相同,在第三个参数输入要比较的字节。
如果没有必到指定的字节,就出结果,那么就不需要比较了。
如果比完规定的字节没出结果,那么就返回0。
2.strstr
在很多时候,我们会在一堆字符串中寻找指定的字符串。strstr函数就可以返回字符串中指定串的起始位置,如果有多个满足要求的串,那么就返回第一起始位置
函数接口
strstr函数的使用,很轻松就找到指定字符串,下面我们就来尝试写一个strstr函数
思路
例如:在str1="abbcdef",寻找str2="bcd"
首先一个指针cur找到第一个相同的位置,即在str1中,找到字符b的地址,
接着保存这个b的地址cur,然后再来俩个指针p1和p2,分别指向str1保存的位置和str2的起始位置
如果比对的字符都相同,让p1和p2都同时往后走,一旦有不同或者遇到'\0'就跳出这次比对
检查是否遇到'\0',否则就要开始新一轮的比对
让cur往后走,直到遇见第一个相同的元素,再进行新一轮的比对
如果cur指针将str1字符串都走完了,还没比对成功,那么就不存在符合要求的串,就返回空指针
接着就来看看代码
//模拟实现strstr
//abbcdef
// bcd
#include<assert.h>
char* my_strstr(char* des, const char* sour)
{
assert(des && sour);
//双指针同时走
char* cur1 = des;
char* cur2 = sour;
char* head = des;//保存起点
while (*head)
{
cur1 = head;
cur2 = sour;
while (*cur1 == *cur2 && *cur1 && *cur2)
{
cur1++;
cur2++;
}
if (*cur2 == '\0')//第二指针走到尾,找到了
{
return head;
}
head++;
}
return NULL;
}
三、总结
在运用上,strtok函数比较麻烦,在一次分割后,函数会保存起始位置,第二次分割时第一个参函数要为NULL。
在模拟实现,strstr相对麻烦,涉及到多个指针,俗话说“好记性不如烂笔头”大家可以动手比对一番,加深对函数的印象。
我是凡凡,一名努力学习C++的博主,如果有哪里不足的地方,请大家指出,我会认真反思!感谢大家的阅读。
代码++
offer++