Files
BlogPosts/Collection/YoudaoyunNotes/02C语言/10-函数进阶.md

12 KiB
Raw Blame History

一、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;
}
  • 注意:
  1. 这两个函数的功能,都是将 src 中的字符串,复制到 dest 中。

  2. strcpy() 没有边界控制,因此可能会由于 src 的过长而导致内存溢出。

  3. 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]);
  • 注意:
  1. 这两个函数的功能,都是在指定的字符串 s 中,试图找到字符 c。

  2. strchr() 从左往右找strrchr() 从右往左找。

  3. 字符串结束标记 \0 被认为是字符串的一部分。

练习

自己封装函数实现strlenstrcatstrtokstrstrstrcpystrcmp函数的功能。

晚上作业

  1. 定义一个长度为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. 输入1增加书籍名称可以连续添加多本

  4. 输入2删除数书籍名称删除一本或全部删除

  5. 输入3修改书籍名称

  6. 输入4查找书籍名称模糊查找或精确查找

  7. 输入5显示所有书籍

  8. 输入0退出系统

    只要系统未退出则可以继续重复进行,直至系统退出。

提示char *book_name[1000]; // 表示表示最多可存放1000书书名的长度自己设计。

设置终端信息字体及颜色