C语言本身是没有字符串类型的,字符串通常放在 常量字符串 中或者 字符数组 中。
字符串常量 适用于那些对它不做修改的字符串函数。
目录
一、strlen函数
1、函数介绍:
2、模拟实现strlen
(1)、计数器方法
(2)、指针减指针方法
(3)、递归方法
二、strcpy
1、函数介绍
2、模拟实现strcpy
三、strcat
1、函数介绍
2、模拟实现strcat
四、strcmp
1、函数介绍
2、模拟实现strcmp
五、strncpy
1、函数介绍
2、模拟实现strncpy
六、strncat
1、函数介绍
2、模拟实现strncat
七、strncmp
1、函数介绍
2、模拟实现strncmp
八、strstr
1、函数介绍
2、模拟实现strstr
中途随笔记:
1、函数介绍:
用途:求字符串的长度
格式: size_t strlen(const char* str); ( size_t代表无符号整形:size_t==unsigned int )
- 字符串以'\0'作为结束标志,strlen函数返回的是在字符串中的'\0'前面出现的字符个数(不包含‘\0’);
- 参数指向的字符串必须以'\0'结束;
- 注意函数的返回值是size_t,是无符号的(易错)。
用简单的代码来展示下strlen的用途:
int len = strlen("abcdef");
printf("字符串“abcdef”的长度是:%d\n",len);
//输出结果为:
// 字符串“abcdef”的长度是:6
strlen函数是通过字符串末尾的 '\0' 来计算长度的,也就是说,只有碰到了'\0',strlen函数才会认为这个字符串结束了,如果没有'\0'结尾的话,strlen就会一直往回计算,直到碰到'\0'。
char arr[] = { 'a','b','c','d','e','f' };
int len1 = strlen(arr);
printf("arr 的长度是:%d\n",len1);
//输出结果为
// arr的长度是:(随机值)
//如下图
还有需要注意的一个地方,那就是strlen的返回类型是无符号整形,我们看以下代码:
if (strlen("abc") - strlen("abcdef") > 0)
printf("strlen返回的是一个无符号整数\n");
else
printf("strlen返回的是一个有符号整数\n");
//输出结果为:
// strlen返回的是一个无符号整数
因为strlen返回的都是无符号整数,所以两个无符号数进行运算所得的值肯定也是一个无符号数,那肯定就是一个大于0的整数,所以if里判断为真,输出上一句话。
但是这里要注意,使用strlen函数时一定要引入string.h头文件,不然的话,上面的代码就是输出下面那一句了:
//模拟实现strlen
//1、计数器
int my_strlen(const char* arr) {//计算长度不能改变源字符串内容,所以加个const稳妥
int count = 0;
while (*arr) {//判断指针指向的内容是否等于'\0'也就是是否等于0,等价于*arr!='\0'
count++;
arr++;
}
return count;
}
int main() {
char arr[] = "abcdefghi";
int len=my_strlen(arr);
printf("arr 的长度是:%d\n", len);
return 0;
}
//模拟实现strlen
//2.指针减指针
int my_strlen(const char* arr) {
char* front = arr;//前指针
char* rear = arr;//后指针
while (*rear) {//直到后指针指向了'\0'
rear++;
}
return rear - front;//此时两指针相减就是字符串的字符个数也就是长度
}
int main() {
char arr[] = "abcdefghi";
int len=my_strlen(arr);
printf("arr 的长度是:%d\n", len);
return 0;
}
//模拟实现strlen
//3、递归
int my_strlen(const char* arr) {
if (*arr == '\0')
return 0;
return 1 + my_strlen(arr + 1);
}
//主函数
int main() {
char arr[] = "aasdvs daci";
int len=my_strlen(arr);
printf("arr 的长度是:%d\n", len);
return 0;
}
1、函数介绍
用途:复制源字符串到一个新字符串里
格式:char* strcpy(char* destination,const char* source);
//destination是目标字符串,source是源头字符串
- 源字符串必须以'\0'结束
- 会将源字符串中的'\0'拷贝到目标空间
- 目标空间必须足够大,以确保能存放源字符串
- 目标空间必须可变
用简单的代码展示下strcpy的用途:
char arr1[] = "abcdef";
char arr2[] = "hello";
printf("原来的arr1==%s\n", arr1);
strcpy(arr1, arr2);
printf("新来的arr1==%s\n", arr1);
//输出结果为:
// 原来的arr1==abcdef
// 新来的arr1==hello
在使用strcpy函数时,也要注意'\0'的存在,源字符串必须以'\0'结尾,不然的话程序会出错:
strcpy的 实现原理,它并没有把arr1全部变成了和arr2一模一样,它只是把前面与arr2长度相等的个数的字符串变成了arr2的字符串:
并且,目标字符串的空间必须大于源字符串,也就是说,arr1的空间必须比arr2大,不然怎么能容得下arr2呢:
还需要注意一点,目标的空间是要可变的,意思就是,目标字符串不能是一个常量字符串,不然程序也会崩溃:
//1.模拟实现strcpy——简陋版
void my_strcpy(char* dest, char* src) {
while (*src) {//等价于*src!='\0'
*dest = *src;//将源字符串的每一个字符赋值给目标字符串
dest++;
src++;
}
//当退出循环时,此时的src指向的是源字符串的'\0'并且并没有拷进目标字符串,
*dest = *src;//所以在循环结束后将他考进
}
//2.模拟实现strcpy——精简版
void my_strcpy(char* dest, const char* src) {
//这个循环条件的值就是*dest的值,当他拷贝完src里面的\0时,就会退出循环了
while (*dest++ = *src++) {
;
}
}
//主函数
int main() {
char arr1[] = "abcdefghikj";//目标字符串空间必须是可变空间,必须比源字符串空间大
char arr2[] = "hello";
strcpy(arr1, arr2);
printf("%s", arr1);//hello
return 0;
}
1、函数介绍
用途:给目标字符串的后面追加一个字符串
语法:char* strcat(char* destination,const char* source);
//destination 是目标字符串,source 是源头字符串
- 源字符串必须以'\0'结束
- 目标空间必须足够大,能容下源字符串的内容
- 目标空间必须可更改
- 不能自己给自己追加
用简单的代码展示下strcat的用途:
char arr1[30] = "hello";
char arr2[] = "world";
printf("原来的arr1==%s\n", arr1);
strcat(arr1, arr2);
printf("新来的arr1==%s\n", arr1);
//输出结果为:
// 原来的arr1==hello
// 新来的arr1==helloworld
(1)、在使用strcat时,也要注意源字符串的'\0':
(2)、目标空间必须足够大:
(3)、并且目标空间是可以更改的,不然程序会崩溃:
(4)、假如一个字符串自己给自己追加是什么样子?依旧会崩溃:
//模拟实现strcat
char* my_strcat(char* dest, const char* src) {
//最后需要返回dest,所以先用个ret先把dest存起来
char* ret = dest;
//1、找到目标字符串的'\0'
while (*dest != '\0') {
dest++;
}
//2、追加
while (*dest++ = *src++) {
;
}
return ret;
}
//主函数
int main() {
char arr1[30] = "hello";
char arr2[] = "world";
strcat(arr1, arr1);
printf("%s\n",arr1);
return 0;
}
1、函数介绍
用途:比较两个字符串的大小
语法:int strcmp(const char* str1,const char* str2);
- 标准规定:
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回等于0的数字
- 第一个字符串小于第二个字符串,则返回小于0的数字
用简单的代码展示下strcmp的用途:
char* p1 = "abc";
char* p2 = "abd";
int ret = strcmp(p1, p2);
if (ret > 0)
printf("p1>p2\n");
if (ret == 0)
printf("p1==p2\n");
if (ret < 0)
printf("p1
(1)、strcmp比较的不是字符串的长度大小,而是字符的ASCII码值, 以第一个不相等的字符为准,如:
前面两个字符串的字符a和字符b都相等,所以继续往后比较,然后因为字符'c'的ASCII码值比字符'h'的ASCII码值小,所以会返回一个负数,代表着p1<p2。
(2)、当p1与p2不相等时,strcmp返回的值不一定就是-1和1。
strcmp的返回值除了0就是一个大于0或者小于0的数,但不一定是1和-1,上面的程序输出的是-1那是因为编译器的问题,实际上不是-1,应该是两个ASCII码值的差。
//模拟实现strcmp
//my_strcmp函数
int my_strcmp(const char* str1, const char* str2) {
//比较,两串一直相等到'\0'时就返回0,说明他俩相等
//不等就直接退出循环
while (*str1 == *str2) {
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
//退出循环了说明只能大于和小于了
if (*str1 > *str2)
return 1;//大于
else
return -1;//小于
}
//主函数
int main() {
char* p1 = "abc";
char* p2 = "abd";
int ret = my_strcmp(p1, p2);
printf("这个返回值是:%d\n", ret);
if (ret > 0)
printf("他代表着:p1>p2\n");
if (ret == 0)
printf("他代表着:p1==p2\n");
if (ret < 0)
printf("他代表着:p1
上面这个程序是用1和-1来表示大于小于0,也可以直接输出实际的大于小于0的数:
return *str1-*str2;
//把上面my_strcmp程序的if else语句直接换成这句
1、函数介绍
用途:拷贝指定长度的字符
语法:char* strncpy(char* destination,const char* source,size_t num);
//destination 是目标字符串,source 是源头字符串,num是指定长度
- 拷贝num个 字符从源字符串到目标空间
- 如果源字符串的长度小于num,则拷贝完字符串后,在目标的后面追加0,直到num个
- 以及strcpy的各种注意
用简单的代码展示下strncpy的用途:
char arr1[10] = "abcdefe";
char arr2[] = "hello";
printf("原来的arr1:%s\n", arr1);
strncpy(arr1, arr2, 4);
printf("新来的arr1:%s\n", arr1);
//输出结果:
// 原来的arr1:abcdefe
// 新来的arr1:hellefe
然后我们看一下内存中 arr1的变化:
能看得出来,strncpy也是把目标字符串里的指定的长度的字符变成源字符串里的字符,与strcpy不同的是:strcpy是把整个目标字符串拷贝给了目标字符串,包括'\0',但是strncpy不是,它只把指定长度的字符拷给目标字符串。
那如果这个指定的长度大于了源字符串的长度怎么办?会自动补上'\0',差多少补多少。
从图看出,arr2的长度只有5,加上一个'\0'也只有6,而我们要拷贝8个字符去arr1里,显然还差两个字符,到最后输出了arr2的字符串,那我们再看内存:
看得出,在arr2的基础上,又多了两个'\0',这就是strncpy自己补上去的两个'\0'。
//模拟实现strncpy
//my_strncpy函数
char* my_strncpy(char* str1, const char* str2, int num) {
//先用一个 指针把原来的指针存起来,不然函数过后字符串就会被改变了
char* dest = str1;
//num和str2的值都不能为0,任一为0退出循环
while (num && *str2) {
*str1 = *str2;
num--;
str1++;
str2++;
}
//上面循环退出时,并没有把'\0'拷贝走
if (num != 0) {
while (num--) {
*str1 = '\0';
str1++;
}
}
return dest;
}
//主函数
int main() {
char arr1[20] = "abcdefeghi";
char arr2[] = "hello";
printf("原来的arr1:%s\n", arr1);
strncpy(arr1, arr2, 8);//arr2位数不够会补'\0'
printf("新来的arr1:%s\n", arr1);
return 0;
}
上面的my_strncpy函数是比较基础的,还可以优化优化:
//模拟实现strncpy
//优化my_strncpy函数
char* my_strncpy(char* str1, const char* str2, int num) {
char* dest = str1;
while (num && (*str1++=*str2++)) {
num--;
}
//上面循环退出时,已经拷走了'\0'
if (num) {
while (--num) {
*str1++ = '\0';
}
}
return dest;
}
这两个函数的区别就在于num--和--num。
1、函数介绍
用途:追加指定长度的字符串
语法:char* strncat(char* destination,const char* source,size_t num);
- 在目标字符串的'\0'后追加,会覆盖当前'\0',并追加完后给上 一个'\0'
- 追加的长度比源字符串长的话,只追加一个源字符串
- 以及strcat的相关注意
用简单的代码展示下strncat的用途:
char arr1[10] = "hello";
char arr2[] = "world";
printf("原来的字符串:%s\n", arr1);
strncat(arr1, arr2, 4);
printf("新来的字符串:%s\n", arr1);
//输出结果:
// 原来的字符串:hello
// 新来的字符串:helloworl
我们先看看内存里的情况:
和strcat一样,都是从目标字符串的'\0'开始追加,并且覆盖这个'\0',我们先看看strncat是不是根据'\0'来进行追加的:
显然,两图比较可以看出,strncat确实是根据'\0'来进行追加的,这和strcat是一样的,并且strncat只追加给定长度的字符串。
要是给定的要追加的长度大于我源字符串的长度怎么办?是像strncpy那样自动补加'\0'吗?并不是,如果长度大于源字符串长度,那么只会追加一个字符串,并不会另加其他:
显然,指定长度为8时,并没有在拷贝后的字符串里出现多余的'\0',说明strncat不会像strncpy那样自动填补差的'\0'。
//模拟实现strncat
//my_strncat函数
char* my_strncat(char* str1, const char* str2, int num) {
char* dest = str1;
//先找到目标字符串的'\0'
while (*str1++) {
;
}
//因为上面多加了一次所以需要减回去
str1--;
//然后从'\0'的地方拷贝
while (num--&&(*str1++ = *str2++)) {
;
}
return dest;
}
//主函数
int main() {
char arr1[20] = "hello\0xxxxxxxx";
char arr2[] = "world";
printf("原来的字符串:%s\n", arr1);
strncat(arr1, arr2, 8);
printf("新来的字符串:%s\n", arr1);
}
1、函数介绍
用途:比较指定长度的字符串
语法:int strncmp(const char* str1,cosnt char* str2,size_t num);
- 比较前num个字符
- 比较到出现一个字符不一样或者一个字符串结束或者num各字符全部比较完
- 以及strcmp相关注意
用简单的代码展示下strncmp的用途:
const char* p1 = "abcdefg";
const char* p2 = "abcf";
int ret1 = strncmp(p1, p2, 4);
printf("%d\n", ret1);
//输出结果:
// -1
//因为第四个字符‘d’小于‘f’
int strncmp(const char* str1,cosnt char* str2,size_t num);
这里的num表示的是要比较的字符的个数,比如num是3的话,那么就只比较p1、p2的前三个字符,也就是abc,那么两个字符串的前三个字符都相等,所以返回值是0:
如果num的值大于两个字符串的长度或者大于任意一个字符串的长度的话,那么只会比较到:
strncmp的返回值规则与strcmp的规则是一样的:
//模拟实现strncmp
//my_strncmp函数
int my_strncmp(const char* p1, const char* p2,int num) {
//以num作循环次数条件
//一有不同的字符就直接返回
for(int i=0;i *p2)
return 1;
if (*p1 < *p2)
return -1;
//指针移到下一个字符
p1++;
p2++;
}
//退出循环说明两个字符串相同
return 0;
}
//主函数
int main() {
const char* p1 = "abcdefg";
const char* p2 = "abcf";
int ret1 = strncmp(p1, p2, 4);
int ret2 = my_strncmp(p1, p2, 4);
printf("%d %d\n", ret1,ret2);
return 0;
}
1、函数介绍
用途:找到子串在一个字符串中第一次出现的位置。
语法:char* strstr(const char* p1,const char* p2);
- 返回的是子串在主串中的第一个字符的位置
用简单的代码展示下strstr的用途:
char* p1 = "abbcdef";
char* p2 = "bbc";
char* ret1 = strstr(p1, p2);
if (ret1 == NULL)
printf("子串不存在\n");
else
printf("%s\n", ret1);
//输出结果:
// bbcdef
//返回的是子串bbc的第一个字符的位置,也就是b的位置
如果p2是p1的子串,就会返回p1的位置。
上图可以看出位置与字符串的关系,是该字符的位置也是以该字符为开头的字符串的位置,所以打印返回的值时是打印的剩余的字符串:
如果不是子串,程序就会返回一个NULL。
#include
//模拟实现strstr
//第一种方法:
char* my_strstr(const char* p1, const char* p2) {
//如果子串是空串直接返回主串
if (*p2 == '\0')
return p1;
//新建两个指针
char* s1 = p1;//主串指针,s1作移动指针,p1函数返回值的指针
char* s2 = p2;//子串指针,s2作移动指针,p2一直指向子串的第一个字符不变
//当主串指针没有到达最后'\0'处
while (*s1) {
//如果两指针指向的字符相同,指针后移继续比较
if (*s1 == *s2) {
s1++;
s2++;
}
//如果不同,p1指向当前s1后移一个的位置处,s2变为原位
else {
p1 = ++s1;
s2 = p2;
}
//当s2直到了'\0'并且两个字符串的两个指针之差相等时,说明p2是p1的子串
if (*s2=='\0'&&(s1 - p1) == (s2 - p2))
return p1;
}
//退出循环说明p2不是p1的子串,返回null
return NULL;
}
//第二种方法:
char* my_strstr(const char* p1, const char* p2) {
//如果子串是空串直接返回主串
if (*p2 == '\0')
return p1;
//创建三个指针
char* s1 = p1; //代替主串
char* s2 = p2; //代替子串
char* cur = p1; //辅助指针,用来表示可能匹配成功的位置
//当主串指针没有指到'\0'时
while (*s1) {
//如果后面有匹配失败,那么s1就回到第一次匹配成功的地方
s1 = cur;
//如果后面匹配失败,s2回到p2开头的地方,重新匹配
s2 = p2;
//在两个字符串都没有比较完时并且两个字符相等
while (*s1!='\0' && *s2 != '\0' && (*s1 == *s2)) {
s1++;
s2++;
}
//如果子串比较到了最后的'\0'处,那说明前面的字符都匹配成功了
if (*s2=='\0')
return cur;//返回第一个字符的位置
//到这说明没有匹配成功,所以将cur向后移动一个
cur++;
}
//退出循环说明p2不是p1的子串,返回null
return NULL;
}
//主函数:
int main() {
char* p1 = "abbcdef";
char* p2 = "bbc";
char* ret1 = strstr(p1, p2);
char* ret2 = my_strstr(p1, p2);
//可以用来比较自己写的strstr与库函数的结果一不一样
if (ret1 == NULL)
printf("子串不存在\n");
else
printf("%s\n", ret1);
if (ret2 == NULL)
printf("子串不存在\n");
else
printf("%s\n", ret2);
return 0;
}
1、用字符数组存一个字符串时,用键盘输入字符串,会自动将每一个字符分配到数组每一个空间,而整形数组不行。(getchar和scanf结果都一样)
2、NULL 代表的是 空指针,Null或NUL 代表的是 '\0';