12 KiB
一、C语言函数库
- 概念:在C语言发展过程中收录了很多经典的数据操作方法----函数,将这些函数收录归纳汇总为开发人员方便使用的API接口(函数),如下图所示的各种操作接口库。
二、字符串操作函数
-
使用三部曲
-
确认头文件
-
确认函数功能
-
确认函数的参数与返回值
-
C语言标准字符串函数库,头文件"#include <string.h>"
- 函数strlen
char *p = "www.yueqian.edu.com.cn";
printf("粤嵌官网的地址长度是:%d\n", strlen(p));
- 函数strcat与strncat
-
注意:
-
这两个函数的功能一样,都是将src字符串复制到dest的末尾。
-
strcat()没有边界控制,因此可能会由于src字符串过长导致dest无法保存从而导致内存溢出。
-
strncat()有边界控制,可以限制拼接字符的格式,保证dest不会因为越界而导致内存溢出。
-
更加值得推荐的字符串拼接函数sprintf()与snprintf(),头文件"#include <stdio.h>"
- 要点:sprintf与snprintf不光可以拼接字符串还可以实现将其他数据类型也添加到字符串中,sprintf对目标字符串的长度没有现在条件又内存溢出风险,但snprintf没有。
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
-
注意:
-
该函数会将改变原始字符串 str,使其所包含的所有分隔符变成结束标记 ‘\0’ 。
-
由于该函数需要更改字符串 str,因此 str 指向的内存必须是可写的。
-
首次调用时 str 指向原始字符串,此后每次调用 str 用 NULL 代替。
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()按照格式提取字符串中的内容
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
char *str = "正午十二点,柏油路面被晒得发软。便利店的冷柜吐出白雾,穿校服的女孩咬着冰棒跑过,"
"塑料包装纸在风里打着旋儿,最后贴在墙角那丛半枯的狗尾草上。"
"远处的施工队歇了工,起重机的吊臂在烈日里投下细长的影子,像根被晒蔫的芦苇。";
/* 在字符串中找子串 */
char *p = strstr(str, "柏油路"); // 从前往后查找子串,返回子串第一次出现位置
char *q = strstr(p+1, "你的女孩");// 若未找到则返回NULL
if(p != NULL)
puts(p);
if(q != NULL)
puts(q);
函数strcpy与strncpy
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;
}
- 注意:
-
这两个函数的功能,都是将 src 中的字符串,复制到 dest 中。
-
strcpy() 没有边界控制,因此可能会由于 src 的过长而导致内存溢出。
-
strncpy() 有边界控制,最多复制 n+1 个字符(其中最后一个是 ‘\0’ )到 dest 中。
函数strcmp与strncmp
#include <stdio.h>
#include <string.h>
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码值<s2中字符的ASCII码值则返回值小于0
// printf("%d\n",ret);
if(ret == 0)
printf("s1与s2相等\n");
else if(ret > 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
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]);
- 注意:
-
这两个函数的功能,都是在指定的字符串 s 中,试图找到字符 c。
-
strchr() 从左往右找,strrchr() 从右往左找。
-
字符串结束标记 ‘\0’ 被认为是字符串的一部分。
练习
自己封装函数实现strlen,strcat,strtok,strstr,strcpy,strcmp函数的功能。
晚上作业
- 定义一个长度为20的整型数据生成随机数对这个数组进行初始化,编写排序函数对这个数据中的数据进行从小到大排序
-
要求编写五种排序函数:冒泡排序、选择排序、插入排序、快速排序、希尔排序
-
提示:
-
各种排序算法的时间复杂度与****空间复杂度
-
冒泡排序
-
顺序:两个数据位置符合排序要求
-
逆序:两个数据位置不符合排序要求
-
思路:从头到尾让两个相邻数据进行比较,顺序保持不变,逆序交换位置,经过一轮比较序列中具有一个“极值”将被挪至末端。
-
插入排序
-
思路:假设数列前面有i个节点的序列是有序的,那么就从第i+1个节点开始,插入到前面i个节点中的合适位置。由于序列的第一个节点始终视为有序,所以实在从第二个节点开始。
-
选择排序
-
在无序序列中依次从头到尾挑选合适的节点放入有序序列。
-
快速排序
-
快排是一直典型的递归思想,相比较其他排序它需要跟多的空间,理论上时间效率是最高的。
-
思想:在待排序序列中选取一个数据,作为“支点”,然后其他数据与支点比较,(升序)比支点小的放左边,比支点大的放右边,全部比较完后支点位于两个序列的中间,这叫一次划分(partition)
- 一次划分之后,序列的内部也许无序,但是左右序列与支点三者间,形成了一种基本有序状态,接下来使用相同的思路,递归的对左右序列进行排序,直到子序列的长度小于等于1为止;
-
希尔排序
-
插入排序的改进版本,普通插入排序是从第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、
结果
-
将自己写的字符串操作函数进行吸收
-
制作一个图书管理系统,要求:
-
输入1:增加书籍名称(可以连续添加多本)
-
输入2:删除数书籍名称(删除一本或全部删除)
-
输入3:修改书籍名称
-
输入4:查找书籍名称(模糊查找或精确查找)
-
输入5:显示所有书籍
-
输入0:退出系统
只要系统未退出则可以继续重复进行,直至系统退出。
提示:char *book_name[1000]; // 表示表示最多可存放1000书,书名的长度自己设计。


















