# 一、C语言函数库 - 概念:在C语言发展过程中收录了很多经典的数据操作方法----函数,将这些函数收录归纳汇总为开发人员方便使用的API接口(函数),如下图所示的各种操作接口库。 ![](images/WEBRESOURCE607893845793236f767da405d9c9a4d5image.png) # 二、字符串操作函数 - 使用三部曲 - 确认头文件 - 确认函数功能 - 确认函数的参数与返回值 - C语言标准字符串函数库,头文件"#include " ![](images/WEBRESOURCE104cae287459e90d523049be3c9ed591image.png) - **函数strlen** ![](images/WEBRESOURCE0b5032b3e1de49ca903bd2db77d153e4b5ee282015ce47bc0f3295c8193891e2.png) ```c char *p = "www.yueqian.edu.com.cn"; printf("粤嵌官网的地址长度是:%d\n", strlen(p)); ``` - **函数strcat与strncat** ![](images/WEBRESOURCEeb0dc101742ac85592adc21845b91dab6f8df56f18a43f648269080ac22c4c87.png) - 注意: - 这两个函数的功能一样,都是将src字符串复制到dest的末尾。 - strcat()没有边界控制,因此可能会由于src字符串过长导致dest无法保存从而导致内存溢出。 - strncat()有边界控制,可以限制拼接字符的格式,保证dest不会因为越界而导致内存溢出。 - 更加值得推荐的字符串拼接函数sprintf()与snprintf(),头文件"#include " ![](images/WEBRESOURCE62638d6f9956908d7b93dd99d70bcc61image.png) ![](images/WEBRESOURCEb4578182300ccebca038b5640e577b8eimage.png) - 要点:sprintf与snprintf不光可以拼接字符串还可以实现将其他数据类型也添加到字符串中,sprintf对目标字符串的长度没有现在条件又内存溢出风险,但snprintf没有。 ```c char str1[10] = "温度:";     int temp = 27;     char buf[50];     // sprintf(buf,"%s%d随机数据啊科技时代粉红色大家发货的撒扩大飞机和\r\n", str1, temp); // 无长度控制     // printf("buf:%s", buf);     snprintf(buf,sizeof buf,"%s%d随机数据啊科技时代粉红色大家发货的撒扩大飞机和\r\n", str1, temp);  // 有长度控制     printf("buf:%s", buf); ``` - **函数strtok** ![](images/WEBRESOURCEf54011d029f949fe3f754b46aa3853cbfc1fc8c6aeff83d2c08b1198d0df3ed6.png) - 注意: - 该函数会将改变原始字符串 str,使其所包含的所有分隔符变成结束标记 ‘\0’ 。 - 由于该函数需要更改字符串 str,因此 str 指向的内存必须是可写的。 - 首次调用时 str 指向原始字符串,此后每次调用 str 用 NULL 代替。 ```c char s[20] = "www.yueqian.com.cn"; char *p = strtok(s, "."); // 首次调用时,s 指向需要分割的字符串 while(p != NULL) { printf("%s\n", p); p = strtok(NULL, "."); // 此后每次调用,均使用 NULL 代替。 } ``` ***注:上述代码的运行结果就是将字符串 s 拆解为"www"、“yueqian”、“com” 和 “cn”*** - 其他子串提取:sscanf()按照格式提取字符串中的内容 ![](images/WEBRESOURCE226669d81bf2dc869c23059ac82f7a8eimage.png) ```c char str[] = "www yueqian-edu com cn"; char str1[4]; char str2[20]; sscanf(str,"%s %s",str1,str2); printf("str1:%s\n", str1); ``` - **函数strstr** ![](images/WEBRESOURCE54b672d577364079ba70fd45152a12e9stickPicture.png) ```c char *str = "正午十二点,柏油路面被晒得发软。便利店的冷柜吐出白雾,穿校服的女孩咬着冰棒跑过,"                 "塑料包装纸在风里打着旋儿,最后贴在墙角那丛半枯的狗尾草上。"                 "远处的施工队歇了工,起重机的吊臂在烈日里投下细长的影子,像根被晒蔫的芦苇。";         /* 在字符串中找子串 */     char *p = strstr(str, "柏油路");  // 从前往后查找子串,返回子串第一次出现位置     char *q = strstr(p+1, "你的女孩");// 若未找到则返回NULL     if(p != NULL)         puts(p);         if(q != NULL)         puts(q); ``` ## **函数strcpy与strncpy** ![](images/WEBRESOURCE4bf7c30b9e994bda856db1440425e6fcstickPicture.png) ```c int main(int argc, char *argv[]) {     char *name = "张三丰";     char name1[20] = "123465dsdskfhjdsahs";     // 不能把一个进行字符串赋值     // name1 = name;     strcpy(name1, name);  // 将name的内容全部内容包括'\0'都拷贝给name1的内存中     puts(name1);     puts(name1+10);     strncpy(name1, name, sizeof(name1));// 将name的内容sizeof(name1)字节部分内容包括'\0'都拷贝给name1的内存中,不足的不会拷贝     puts(name1);     return 0; } ``` - 注意: 1. 这两个函数的功能,都是将 src 中的字符串,复制到 dest 中。 1. strcpy() 没有边界控制,因此可能会由于 src 的过长而导致内存溢出。 1. strncpy() 有边界控制,最多复制 n+1 个字符(其中最后一个是 ‘\0’ )到 dest 中。 ## **函数strcmp与strncmp** ![](images/WEBRESOURCE07b09d5113504e099c4fbfd3ef55f632stickPicture.png) ```c #include #include void fun(char *p) {     char *q = "abc";     if(p==q)     {         printf("p与q相等\n");     }     else     {         printf("p与q不相等\n");     } } int main(int argc, char *argv[]) {     // char *str1 = "abc";     // char *str2 = "abc"; // 相同的常量在内存中只有一份     // fun(str1);     // if (str1 == str2) // 比较的是指针指向的目标是否相同     // {     //     printf("str1与str2相等\n");     // }     // else     // {     //     printf("str1与str2不相等\n");     // }     char s1[] = "abcacccaddc";     char s2[] = "abca";     // if (s1 == s2)// 比较的是s1和s2的内存地址是否相同     // {     //     printf("s1与s2相等\n");     // }         int ret = strcmp(s1,s2); // 不是比较长短,而是比较两个字符串内容的大小(ASCII码值),一直比较到有结果为止                              // 若s1中字符的ASCII码值>s2中字符的ASCII码值则返回值大于0                              // 若s1中字符的ASCII码值与s2中字符的ASCII码值全部相等则返回值等于0                              // 若s1中字符的ASCII码值 0)         printf("s1大于s2\n");     else         printf("s1小于s2\n");     ret = strncmp(s1,s2, 4); // 选择性比较,可以设置比较的字符长度     // printf("%d\n",ret);     if(ret == 0)         printf("s1与s2相等\n");     else if(ret > 0)         printf("s1大于s2\n");     else         printf("s1小于s2\n");     return 0; } ``` - 注意: - 比较字符串大小,实际上比较的是字符的 ASCII码值的大小。 - 从左到右逐个比较两个字符串的每一个字符,当能“决出胜负”时立刻停止比较(s1的字符ascii码值减去s2字符的ascii码值返回差值)。 ## **函数strchr与strrchr** ![](images/WEBRESOURCEa0517f24461b40ebad5fb8cb35e5046estickPicture.png) ```c char *str = "正午十二点,柏油路面被晒得发软。便利店的冷柜吐出白雾,穿校服的女孩咬着冰棒跑过,"                 "塑料包装纸在风里打着旋儿,最后贴在墙角那丛半枯的狗尾草上。"                 "远处的施工队歇了工,起重机的吊臂在烈日里投下细长的影子,像根被晒蔫的芦苇。"; /* 在字符串中找字符 */     char *k = strchr(str, ','); // 从前往后查找字符,返回字符第一次出现位置(中文字符不行),没有找到返回NULL     if(k != NULL)         puts(k);     char *j = strrchr(str, '\0'); // 从后往前主要字符,返回字符第一次出现位置(中文字符不行),没有找到返回NULL     if(j != NULL)         puts(j);     printf("%#x\n", j[0]); ``` - 注意: 1. 这两个函数的功能,都是在指定的字符串 s 中,试图找到字符 c。 1. strchr() 从左往右找,strrchr() 从右往左找。 1. 字符串结束标记 ‘\0’ 被认为是字符串的一部分。 ## 练习 自己封装函数实现strlen,strcat,strtok,strstr,strcpy,strcmp函数的功能。 # 晚上作业 1. 定义一个长度为20的整型数据生成随机数对这个数组进行初始化,编写排序函数对这个数据中的数据进行从小到大排序 - 要求编写五种排序函数:冒泡排序、选择排序、插入排序、快速排序、希尔排序 - 提示: - **各种排序算法的****时间复杂度****与****空间复杂度** ![](images/WEBRESOURCEb440b29046c7b9a6f3c4315e2c59b6ffimage.png) - **冒泡排序** - 顺序:两个数据位置符合排序要求 - 逆序:两个数据位置不符合排序要求 - 思路:从头到尾让两个相邻数据进行比较,顺序保持不变,逆序交换位置,经过一轮比较序列中具有一个“极值”将被挪至末端。 ![](images/WEBRESOURCE017da79a24a769cff3e5b4a570845821849589-20171015223238449-2146169197.gif) ```c ``` - **插入排序** - 思路:假设数列前面有i个节点的序列是有序的,那么就从第i+1个节点开始,插入到前面i个节点中的合适位置。由于序列的第一个节点始终视为有序,所以实在从第二个节点开始。 ![](images/WEBRESOURCE0bef0e4f20c74105d00fc30f18338300849589-20171015225645277-1151100000.gif) ```c ``` - **选择排序** - 在无序序列中依次从头到尾挑选合适的节点放入有序序列。 ![](images/WEBRESOURCE5936c4c59a03bf6508f3f2fc7f496572849589-20171015224719590-1433219824.gif) ```c ``` - **快速排序** - 快排是一直典型的递归思想,相比较其他排序它需要跟多的空间,理论上时间效率是最高的。 - 思想:在待排序序列中选取一个数据,作为“支点”,然后其他数据与支点比较,(升序)比支点小的放左边,比支点大的放右边,全部比较完后支点位于两个序列的中间,这叫一次划分(partition) ![](images/WEBRESOURCEe0805f5f18d746ebfe9e7c96c29f5a05image.png) - 一次划分之后,序列的内部也许无序,但是左右序列与支点三者间,形成了一种基本有序状态,接下来使用相同的思路,递归的对左右序列进行排序,直到子序列的长度小于等于1为止; ![](images/WEBRESOURCEb6a0efee1c1bc96325017b5204bf671f849589-20171015230936371-1413523412.gif) ```c ``` - **希尔排序** - 插入排序的改进版本,普通插入排序是从第2个节点开始,依次插入到有序序列中,这种做法在虽然一次成型,但时间效率上不划算,优化思路: - 不严格一个个插入使之有序,而是拉开插入节点的距离,让它们逐步有序,有待排序序列如下: 84、83、88、87、61、50、70、60、80、89 - 第一遍,先区间隔(Δ=5),即依次对以下5组数据进行排序 **84** 84、 84、83、 84、83、88、 84、83、88、87、 **注意:** **50** 50、 50、70、 50、70、60、 50、70、60、80、 **结果:(** **50** 50、 50、70、 50、70、60、 **得到:** **50** 50、 50、61、 50、61、60、 **结果** ![](images/WEBRESOURCE77576e5ba8ad0a92819a29e42a156f4c849589-20180331170017421-364506073.gif) ```c ``` 1. 将自己写的字符串操作函数进行吸收 1. 制作一个图书管理系统,要求: 1. 输入1:增加书籍名称(可以连续添加多本) 1. 输入2:删除数书籍名称(删除一本或全部删除) 1. 输入3:修改书籍名称 1. 输入4:查找书籍名称(模糊查找或精确查找) 1. 输入5:显示所有书籍 1. 输入0:退出系统 只要系统未退出则可以继续重复进行,直至系统退出。 提示:char *book_name[1000]; // 表示表示最多可存放1000书,书名的长度自己设计。 [设置终端信息字体及颜色](https://share.note.youdao.com/s/DydeICJh)