学习目标:
在上一篇博客中,我们已经充分的学习了指针的相关知识,那么这篇博客,我们来重点关注一下字符串函数,主要学习求字符串长度的函数,长度受限制的字符串函数,长度不收限制的字符串函数,以及字符串查找函数,最后再来介绍一下错误信息报告。
之后,我们会将一些函数进行模拟实现,再拓展一下其他内容,C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串类型通常放在常量字符串或者字符数组中,字符串常量适用于那些对它不做修改的字符串函数。所以这篇博客还是比较重要的。
学习内容:
通过上面的学习目标,我们可以列出要学习的内容:
- 求字符串长度的函数,以及手撕代码,模拟实现
- 长度受限制的字符串函数,同样手撕代码,模拟实现
- 长度不受限制的字符串函数,观看其源代码,了解原理
- 字符串查找函数,同样手撕代码,模拟实现
- 介绍一下错误信息报告
小编了解有些人是不会看学习目标,那么我就再说一遍:说起字符串,C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。常量字符串适用于那些对他不做修改的字符串函数。
一、求字符串长度的函数——strlen
1.1 函数介绍
在【初阶C语言3】特别详细地介绍函数中,小编已经介绍了如何查找库函数的参数和具体用法,在这里小编就不多说了。回归主题,我们来看strlen函数。
简单来说,strlen函数是求字符串长度的库函数,传递的参数是要求字符串长度的首元素地址即可。但是要注意的是strlen函数本质上统计的是字符串中 '\0' 之前的字符的个数。看下面的代码进行区分:
#include <stdio.h> #include <string.h> //别忘了引头文件 int main() { char arr1[] = "sdgeghkoe"; char arr2[] = "sdgeg\0hkoe"; printf("strlen(arr1) = %d\n", strlen(arr1)); printf("strlen(arr2) = %d\n", strlen(arr2)); return 0; }
在来看strlen函数返回值的特点,仔细看,在库函数中,strlen函数的返回值的类型是无符号整形(size_t)。怎么理解这个无符号整形呢?我们来看下面的一道题:
int main() { const char* str1 = "abcdfre"; //const修饰,使指针指向的字符串不能被修改 const char* str2 = "fsfe"; if (strlen(str2) - strlen(str1) > 0) { printf("str2 > str1"); } else { printf("str1 > str2"); } return 0; }
这道题按照常理来讲,str2指向的字符串长度比str1指向的字符串长度小,所以这个运算的结果应该是str1 > str2,但为什么结果是str2 > str1,。
因为strlen函数的返回值是无符号整形,两个无符号整形进行相加减的时候是不会出现负数的情况,所以不论这道题的结果是什么,永远都是str2 > str1。
1.2 模拟实现一下strlen函数
模拟实现strlen函数有三种方法:一个是计数器、一个是递归的方法、一个是指针减指针的方法。下面,小编来一一为大家进行讲解:
1.2.1 计数器方法实现strlen函数
1.3 总结一下strlen函数的要点
二、长度不受限制的字符串函数
2.1 strcpy函数
2.1.1 函数介绍
strcpy——string copying,表示的是字符串拷贝的工作,如果我们不清楚,我们可以在网站上进行搜索strcpy函数。
上面的意思是: 将源头的字符串传给目的地的字符串中,包括 '\0'。在使用strcpy函数进行拷贝字符串的时候,源字符串必须要有 '\0'(其是strcpy函数拷贝结束的标志),否则程序将会打印出一些不必要的内容。请看下图进行解释:
基本用法:
//第一种写法 int main() { char arr1[20] = "xxxxxxxxx"; char arr2[] = { 's','a','d','\0'}; printf("%s\n", strcpy(arr1, arr2)); return 0; } //第二种写法 int main() { char arr1[20] = "xxxxxxxxx"; char arr2[] = "sad"; printf("%s\n", strcpy(arr1, arr2)); return 0; }
int main() { char arr1[20] = "xxxxxxxxx"; char arr2[] = { 's','a','d' }; printf("%s\n", strcpy(arr1, arr2)); return 0; }
2.1.2 strcpy函数使用时出现的一些问题
问题一:目的地的字符数组的大小比源头的字符数组的大小要小
代码:
int main() { char arr1[] = { 0 }; char arr2[] = "hello"; printf("%s\n", strcpy(arr1, arr2)); return 0; }
结果:
虽然会弹出警告,但是strcpy函数还是会完成其使命,将源头的字符串拷贝到目的地的字符串中,因为strcpy函数是不受长度限制的函数。
问题二:将源头的字符串拷贝到常量字符串中
代码:
int main() { char* p = (char* )"feifhw"; char arr2[] = "hello"; printf("%s\n", strcpy(p, arr2)); return 0; }
结果:
2.1.3 总结一下strcpy函数的要点
- 源字符串必须以 '\0' 结束
- 在拷贝的过程中,会将源字符串中的 '\0' 拷贝到目标空间中
- 目标空间必须足够大,以确保能存放源字符串
- 目标空间必须可变,不能是常量字符串
2.1.4 模拟实现一下strcpy函数
char* my_strcpy(char* base, const char* serc) //源头的内容不用改,用const修饰 { assert(base && serc); //检查指针是否指空 char* ans = base; while (*base++ = *serc++) { ; } return ans; }
2.2 strcat函数
2.2.1 函数介绍
这个函数是用于字符追加的,如果我们不清楚,我们依然可以进行在网站上查询strcat函数。下面小编我将会为大家用代码示范一下如何使用strcat函数:
基本用法:
int main() { char arr1[20] = "asd"; char arr2[] = "hjk"; printf("%s\n", strcat(arr1, arr2)); return 0; }
在上面的用法中,我们好像已经浅浅地了解了strcat函数的用法,就是将源头的字符串追加到目的地的字符串中。那么这时,我们要来思考一下问题:1)到底是遇见 '\0' 进行追加,还是遇见字符串末尾进行追加呢?2)字符串在拷贝的过程中是否将源头的 '\0' 也拷贝过去呢?3)目的地字符串数组是否要足够大?下面我们来解决一下这些问题:
2.2.2 解决上述strcat函数的相关问题
问题一:到底是遇见什么进行追加(遇见'/0'进行追加)
问题二:在拷贝的过程中将源头的'\0'拷贝过去
int main() { char arr1[20] = "asdxxx\0xxxxx"; char arr2[] = "hjk"; printf("%s\n", strcat(arr1, arr2)); return 0; }
问题三:目的地数组要足够大(与strcpy类似)
int main() { char arr1[] = "asdxxx"; char arr2[] = "hjk"; printf("%s\n", strcat(arr1, arr2)); return 0; }
2.2.3 总结一下strcat函数的要点
- 源字符串必须要以 '\0' 结束,保证能够找到目标空间的末尾
- 目标变量必须要足够大,能容纳下源字符串的内容
- 目标函数必须可以修改
2.2.4 模拟实现一下strcat函数
char* my_strcat(char* str1, const char* str2) { assert(str1 && str2); char* ret = str1; //找到目标空间的末尾 while (*str1 != 0) { str1++; } //数据追加 while (*str1++ = *str2++) { ; } return ret; }
2.2.5 思考一下字符串能和自己追加呢?
显然是不能的,因为如果自己给自己进行追加,会导致字符串的内容发生一些变化,将字符0给堵盖掉,就没有结束标志了,看下图进行理解:
2.3 strcmp函数
2.3.1 函数介绍
strcmp——string compare,表示的是字符串比较的工作,注意这个字符串函数比较的不是字符串的大小,而是对应位置上字符的大小(ASCII值)。我们也可以在网站上搜索strcmp函数。
标准规定:
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回0
- 第一个字符串小于第二个字符串,则返回小于0的数字
要注意的是,这个函数的返回值为int,两个参数都是const char*类型的,因为我只是想要比较两个字符串的大小,不需要进行修改。
基本用法:
int main() { char arr1[] = "dsgfer"; char arr2[] = "dsgfty"; printf("%d\n", strcmp(arr1, arr2)); return 0; }
2.3.2 模拟实现一下strcmp函数
//第一种写法,返回值是1和-1 int my_strcmp(const char* str1, const char* str2) { assert(str1 && str2); while (*str1 == *str2) { if (*str2 == 0) return 0; str1++; str2++; } if (*str1 < *str2) return -1; else if(*str1 > *str2) return 1; } //第二种写法,返回值不一定是1和-1 int my_strcmp(const char* str1, const char* str2) { assert(str1 && str2); while (*str1 && *str2 && *str1 == *str2) { str1++; str2++; } return *str1 - *str2; }
2.4 长度不受限制的字符串函数的特点
这些字符串函数都会将直接完成该完成的任务,不会管目的地字符串数组能否放的下,它都会将放入其中,这些函数是简单粗暴的,而我们学的下几个函数不会像这几个函数一样。
三、长度受限制的字符串函数
我们来看这几个函数strncpy,strncat,strncmp,它们都多了一个n,也就是number。这些函数都会确定几个字符进行该函数的工作,所以说,它们是长度受限制的字符串函数。
3.1 strncpy函数
3.1.1 函数介绍
在和strcpy函数进行比较时,会发现参数部分多了一个num,要和strcpy进行区分一下,所以其作用是拷贝num个字符从源字符串到目标空间中。我们依然可以在网站上搜索strncpy函数。
3.1.2 strncpy函数要注意的要点
要点一:是否在后面拷贝 '\0' ?
这个函数并没有在后拷贝 '\0' ,可以看下面的代码:
int main() { char arr1[] = "xxxxxxxxxxx"; char arr2[] = "asd"; printf("%s\n", strncpy(arr1, arr2, 3)); return 0; }
要点二:如果源字符串的长度小于num,怎么办?
如果源字符串的长度小于num,则在拷贝完字符串之后,在目标的后面追加0,直到num个。
int main() { char arr1[] = "xxxxxxxxxxx"; char arr2[] = "asd"; printf("%s\n", strncpy(arr1, arr2, 6)); return 0; }
3.2 strncat函数
3.2.1 函数介绍
这个函数和strcat函数的功能是一样的,我们同样可以在网站上搜索strncat函数。
基本用法:
int main() { char arr1[20] = "hoojp"; char arr2[20] = "wqoieh"; strncat(arr1, arr2, 4); printf("%s\n", arr1); return 0; }
3.2.2 strncat函数要注意的要点
我们要记住strncat函数将几个字符追加到另一个字符串中,会在后面添加一个 '\0'。看下面的代码:
int main() { char arr1[20] = "hoojp\0xxxxx"; char arr2[20] = "wqoieh"; strncat(arr1, arr2, 4); printf("%s\n", arr1); return 0; }
3.3 strncmp函数
该函数的功能是比较到出现另一个字符不一样或者一个字符串结束或者num个字符全部比较完。看下图的精确含义:
基本用法:
int main() { char arr1[20] = "hjoihsoad"; char arr2[20] = "hjoihjdkp"; int ret = strncmp(arr1, arr2, 5); printf("%d\n", ret); return 0; }
四、字符串查找函数
这些函数还是比较怪的,不同于前面讲述的三类函数,是一下关于字符串内容的操作,我们来一起看看吧!
4.1 strstr函数
4.1.1 函数介绍
strstr——string string,是在字符串中查找子字符串中的操作,根据函数原型,可以发现是在str1中找到str2第一次出现的位置,如果str1中没有str2,就返回NULL。
基本用法:
int main() { char str[] = "This is a simple string"; char* pch; pch = strstr(str, "simple"); printf("%s", pch); return 0; }
4.1.2 模拟实现一下strstr函数
const char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1; const char* s2; const char* cp; cp = str1; while (*cp) { s1 = cp; s2 = str2; while (*s1 == *s2) { if (*s2 == 0) return cp; s1++; s2++; } cp++; } return NULL; }
4.1.3 拓展一下kmp算法
4.2 strtok函数
strtok函数是用来切割字符串的,如果在一个字符串中有分隔符的话,我们可以使用这个函数去按照分隔符将一个字符串分割成好几份。下面来看这个函数的参数:
- sep参数是一个字符串,定义了用作分隔符的字符的集合;
- 第一个参数制定一个字符串,它包含了0个或者多个由字符串中的一个或者多个分隔符分割的标记;
- strtok函数找到str中的下一个标记,并将其用 '\0' 进行结尾,返回一个指向这个标记的指针(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可以修改)
- strtok函数的一个参数不为NULL,函数将找到str中的第一个标记,strtok函数将保存它在字符串中的位置;
- strtok函数的一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记;
- 如果字符串中不存在更多标记,则返回NULL指针。
基本用法:
int main() { char arr[] = "shdakio@iahd.isauh"; char buf[200] = { 0 }; strcpy(buf, arr); const char* p = "@."; char* s = NULL; for (s = strtok(arr, p); s != NULL; s = strtok(NULL, p)) { printf("%s\n", s); } return 0; }
五、错误信息报告函数strerror
strerror函数是将错误码翻译成错误信息,返回错误信息的字符串的起始位置。错误码有是什么,C语言在使用库函数时,如果发生错误,会将错误码放在errno中,errno是全局变量,可直接使用。
基本用法:
int main() { int i = 0; for (int i = 0; i < 10; i++) { printf("%s\n", strerror(i)); } return 0; }
学习产出:
- 求字符串长度的函数,以及手撕代码,模拟实现
- 长度受限制的字符串函数,同样手撕代码,模拟实现
- 长度不受限制的字符串函数,观看其源代码,了解原理
- 字符串查找函数,同样手撕代码,模拟实现
- 介绍一下错误信息报告