C语言之字符函数字符串函数篇(1)

目录

前言

求字符串长度

 strlen

strlen统计的是字符串\0之前的字符串长度

字符指针

strlen的返回值是无符号整型

strlen的三种模拟实现 

计数器

函数递归 

指针_指针

长度不受限制的字符串函数

strcpy

strcpy会将源字符串中的 '\0' 拷贝到目标空间

strcpy参数目标空间必须足够大且可变

strcpy的模拟实现

strcat

strcat有将源头字符串的\0追加过去

strcat字符串自己追加自己 

strcat的模拟实现✔

strcmp

strcpy的返回值

strcpy的模拟实现✔

长度受限制的字符串函数

strncpy

num<

无拷贝'\0'  

num>

strncat

num<

是追加'\0' 

num> 

strncmp

>0

<0

=0

总结


接下来我们进入字符函数和字符串函数的讲解。中秋和国庆即将来临,终于要放假了回家了。

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串 中或者 字符数组 中。 字符串常量 适用于那些对它不做修改的字符串函数.

我们重点讲解:字符串函数 字符函数 内存函数

关于下面的字符串函数我们将从:作用  头文件  参数 返回值 等方面去介绍。

前言

 printf打印字符串是打印\0之前的字符串。看到\0停止打印了

#include<stdio.h>
int main()
{char arr[10] = "abc\0def";printf("%s\n", arr);return 0;
}

 

求字符串长度

 strlen

strlen - C++ Reference

  • strlen是库函数,是用来求字符串长度的
  • strlen求字符串长度本质上统计的是字符串\0之前的字符串长度。
  • 字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前的字符个数(不包含 '\0' )
  • strlen的头文件是#include<string.h>
  • strlen的参数 是 const char *str (把字符串的首元素地址传给strlen)(const 是为了确保库函数strlen里的操作将主函数main的字符串改变了)
  • 参数指向的字符串必须要以 '\0' 结束。
  • strlen的返回值是 size_t 类型的,返回的数值是无符号的整型
  • strlen的模拟实现需要掌握(计数器  函数递归  指针_指针)

strlen统计的是字符串\0之前的字符串长度

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abcdef";size_t ret =strlen(arr1);char arr2[] = "abc\0def";//统计的是\0之前的字符串长度size_t r = strlen(arr2);printf("arr1=%d,arr1=%d", ret, r);return 0;
}

 

字符指针

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abcdef";size_t ret =strlen(arr1);char* p = "abcdef";//这里p存储的就是字符串的首元素地址 起始地址size_t r = strlen(p);printf("ret=%d r=%d", ret, r);return 0;
}

strlen的返回值是无符号整型

#include<stdio.h>
#include<string.h>
int main()
{//          3                 6       if (strlen("abc") - strlen("abcdef") > 0)//正数,3为无符号数是一个很大的数{printf(">\n");}else{printf("<\n");}return 0;
}3为无符号整数的时候
//00000000 00000000 00000000 00000011 原码
//为无符号为所以没有符号位,所以全部取反加1
//11111111 11111111 11111111 11111101 补码
6
//00000000 00000000 00000000 00000110 原码
//11111111 11111111 11111111 11111010 补码//3>6

strlen的三种模拟实现 

计数器
#include<stdio.h>
size_t my_strlen(char* p)
{int count = 0;while (*p){count++;p++;}return count;
}
int main()
{char arr[] = "abcdef";size_t ret = my_strlen(arr);printf("%zd\n", ret);return 0;
}
函数递归 
#include<stdio.h>
size_t my_strlen(const char* p)
{if (*p == '\0')return 0;else{return 1 + my_strlen(p+1);//切记不要p++,++p这种副作用的++}
}
int main()
{char arr[] = "abcdef";size_t ret = my_strlen(arr);printf("%zd", ret);return 0;
}
指针_指针
#include<stdio.h>
int my_strlen(char* p)
{char* r = p;//起始地址while (*p != '\0'){p++;}return p - r;
}
int main()
{char arr[] = "abcdef";int ret=my_strlen(arr);printf("%d", ret);return 0;
}

长度不受限制的字符串函数

下面我们介绍的是长度不受限制的字符串函数。

strcpy

 strcpy - C++ Reference (cplusplus.com)

  • strcpy是库函数,是用来字符串拷贝的,会将源字符串中的 '\0' 拷贝到目标空间。
  • 本质就是 将源头字符串source(包括\0)  拷贝到 目的地空间中的字符串destination
  • strcpy的头文件是 #include<string>
  • strcpy的参数 一个是指向 源头字符串的首元素地址  指针变量source 
  • 源字符串必须以 '\0' 结束。
  • strcpy的另外一个参数是指向 目的地空间字符串的首元素地址 指针变量destination 
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。
  • strcpy函数的返回值是 目标空间的起始地址
  • strcpy的模拟实现
  • 链式访问和assert

strcpy会将源字符串中的 '\0' 拷贝到目标空间

#include<stdio.h>
int main()
{char arr1[20] = "xxxxxxxxxxxxx";printf("%s\n", arr1);char arr2[] = "abcdef";//会将\0拷贝过去,所以不会有xxxxxstrcpy(arr1, arr2);printf("%s", arr1);return 0;
}

strcpy参数目标空间必须足够大且可变

#include<stdio.h>
int main()
{char arr1[1] = { 0 };//不够大char arr2[] = "abcdef";strcpy(arr1, arr2);printf("%s", arr1);return 0;
}#include<stdio.h>
int main()
{char arr1[1] = "hijklem";//常量字符串char arr2[] = "abcdef";strcpy(arr1, arr2);printf("%s", arr1);return 0;
}

strcpy的模拟实现

//逻辑关系
//
{
;
}
//操作符的优先级
//画图最重要

//模拟实现strcpy
#include<stdio.h>
#include<assert.h>
char* my_strcpy( char*dest,  const char* src)
{char* ret = dest;//为了返回目标空间字符串的起始地址,需要先存储起来assert(dest && src);//断言while (*dest++ = *src++)//赋值和等于{;}return ret;
}
int main()
{char arr1[20] = {0};char arr2[] = "abcdef";my_strcpy(arr1, arr2);printf("%s\n", arr1);return 0;
}
//链式访问
int main()
{char arr1[20] = {0};char arr2[] = "abcdef";printf("%s\n", my_strcpy(arr1, arr2));//链式访问return 0;
}//换种写法❌的没有将\0拷贝过去
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{char* ret = dest;//为了返回目标空间字符串的起始地址,需要先存储起来assert(dest && src);//断言while (*src != '\0'){*dest = *src;//不是== 是赋值dest++;src++;}return ret;
}

strcat

strcat - C++ Reference (cplusplus.com)

  • strcat是库函数,是用来字符串追加的
  • strcat是将 源头字符串 (包括\0)  追加到 目的地空间字符串 (覆盖\0) 的后面。
  • strcat的头文件是#include<string.h>
  • strcat 的一个参数是 指向源头字符串的首元素地址 的指针变量 source
  • 源字符串必须以 '\0' 结束。
  • 在拷贝时,需要将源字符串中的'\0'拷贝过去
  • strcat 的另外一个参数是 指向目的地空间的字符串的首元素 的指针变量 destination
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改
  • 目标空间必须得有'\0'(保证能找到目标空间的末尾)
  • strcat的返回值也是 目标空间的起始地址
  • strcat的模拟实现
  • 字符串自己给字节追加,可以吗?strncat

strcat有将源头字符串的\0追加过去

#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "abc";char arr2[20] = "defg";strcat(arr1, arr2);printf("%s\n", arr1);return 0;
}

strcat字符串自己追加自己 

#include<stdio.h>
int main()
{char arr1[20] = "abc";strcat(arr1, arr1);printf("%s\n", arr1);return 0;
}

#include<stdio.h>
char* my_strcat(char* str1, const char* str2)
{char* ret = str1;while (*str1){str1++;}while (*str2 != '\0'){*str1++ = *str2++;}return ret;
}int main()
{char arr1[20] = "abc";char arr2[20] = "defg";my_strcat(arr1, arr1);printf("%s\n", arr1);return 0;
}

 

字符串自己追加自己这种方式。在不同编译器上,有的可以,有的不行。编译器的厂商不同,相同的库函数是由不同程序员实现的,所以库函数内部实现是不同的。最好不要这样写 。后面介绍strncat可以实现这种情况。

strcat的模拟实现✔

#include<stdio.h>
char* my_strcat(char* str1, const char* str2)
{char* ret = str1;while (*str1){str1++;}while (*str2 != '\0'){*str1++ = *str2++;}return ret;
}int main()
{char arr1[20] = "abc";char arr2[20] = "defg";my_strcat(arr1, arr2);printf("%s\n", arr1);return 0;
}

strcmp

 strcmp - C++ Reference (cplusplus.com)

strcmp的返回值如下: 

  •  strcmp是库函数,是用来字符串比较的
  •  本质上:不是比较长度,而是比较对应位置上的字符的大小,用ASCII码值来比较
  •  strcmp的头文件是 #include<string.h>
  •  strcmp 的两个参数 是指向 两个需要比较的字符串 的指针变量str1 和 str2
  •  const修饰指针变量,为了确保strcmp库函数里的操作将主函数main里的字符串改变
  •  strcmp的返回值由上图解释就是:
  1. 第一个字符串 大于 第二个字符串,则返回大于0的数字
  2. 第一个字符串 等于 第二个字符串,则返回0
  3. 第一个字符串 小于 第二个字符串,则返回小于0的数字
  • strcpy的模拟实现 

strcpy的返回值

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abz";char arr2[] = "abq";if (strcmp(arr1, arr2) > 0){printf(">\n");}else if(strcmp(arr1, arr2) < 0){printf("<\n");}else{printf("=\n");}printf("%d", strcmp(arr1, arr2));//不同编译器下的值不同,但是都是以><=0的范围。return 0;
}

strcpy的模拟实现✔

#include<stdio.h>
#include<string.h>
int my_strcmp(char* p1, char* p2)
{while (*p1 == *p2){if (*p1 == '\0')return 0;p1++;p2++;}//!=if (*p1 > *p2)return 1;elsereturn -1;
}
int main()
{char arr1[] = "abz";char arr2[] = "abq";if (my_strcmp(arr1, arr2) > 0){printf(">\n");}else if(my_strcmp(arr1, arr2) < 0){printf("<\n");}else{printf("=\n");}printf("%d", my_strcmp(arr1, arr2));//不同编译器下的值不同,但是都是以><=0的范围。return 0;
}
int my_strcmp(char* p1, char* p2)
{while (*p1 == *p2){if (*p1 == '\0')return 0;p1++;p2++;}//!=return (*p1-*p2);
}

长度受限制的字符串函数

接下来,我们介绍的是在上面函数的基础上,长度受限制的字符串函数 

关于上面的解释过的点,我们将不在介绍,下面我们重点介绍受限制n的差别,和注意点 

对比上面长度不受限制的字符串函数,我们发现多了一个size_t num的参数 ,相对安全一些

strncpy

 strncpy - C++ Reference (cplusplus.com)

  • 拷贝num个字符从源字符串到目标空间。
  • 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个 
  • 不会将'\0'拷贝过去 

num<

#include<stdio.h>
int main()
{char arr1[20] = "xxxxxxxxxxxxxx";char arr2[] = "abcdef";strncpy(arr1, arr2,3);printf("%s", arr1);return 0;
}

无拷贝'\0'  

 那请问是否将'\0'拷贝过去了呢?答案是:没有哦

#include<stdio.h>
int main()
{char arr1[20] = "xxxxxxxxxxxxxx";char arr2[] = "abcdef";strncpy(arr1, arr2,3);printf("%s\n", arr1);strcpy(arr1, arr2);printf("%s", arr1);return 0;
}对比一下:就只知道'\0'有没有拷贝过去
#include<stdio.h>
int main()
{char arr1[20] = "xxxxxxxxxxxxx";printf("%s\n", arr1);char arr2[] = "abcdef";//会将\0拷贝过去,所以不会有xxxxxstrcpy(arr1, arr2);printf("%s", arr1);return 0;
}

调式过后,我们可以发现,当我们num是几,我们就拷贝几个字符过去,并不会将'\0'拷贝  

num>

那当我们的num超过了被拷贝的字符串的字符个数,会发生什么呢? 

#include<stdio.h>
int main()
{char arr1[20] = "xxxxxxxxxxxxxx";char arr2[] = "abc";strncpy(arr1, arr2, 6);printf("%s\n", arr1);return 0;
}

当我们调试发现,即便我们没有num超出了被拷贝字符串的字符个数。但是strnpy仍然会达到num的个数。那就是用'\0'去填补缺少的num的个数,必须满足num的字符个数拷贝过去方可。 

strncat

strncat - C++ Reference (cplusplus.com)

  • 追加num个字符从源字符串到目标空间。
  • 如果源字符串的长度小于num,则追加完源num字符串之后,不会再追加了
  • 会将'\0'追加过去 

num<

#include<stdio.h>
int main()
{char arr1[20] = "abc";char arr2[] = "defghi";strncat(arr1, arr2, 3);printf("%s\n", arr1);return 0;
}

是追加'\0' 

那我们的strncat函数是否追加字符串的同时将'\0'追加过去呢?答案是:有。

#include<stdio.h>
int main()
{char arr1[20] = "abc\0xxxxxxxxxx";char arr2[] = "defghi";strncat(arr1, arr2, 3);printf("%s\n", arr1);return 0;
}
//如果追加了'\0',那么打印abcdef
//如果没有追加'\0',那么打印abcdefxxxxxxx

 

 

经过调试,我们发现在将源字符串追加到目的地空间字符串的后面时,我们把num的字符追加过去之后,同时会将'\0'追加过去。

num> 

 那当我们的num超过了被追加的字符串的字符个数,会发生什么呢? 

#include<stdio.h>
int main()
{char arr1[20] = "abc\0xxxxxxxxxx";char arr2[] = "defghi";strncat(arr1, arr2, 10);printf("%s\n", arr1);return 0;
}

 

当我们调试发现,当num超过源字符串里的元素个数,strncat的源字符串有多少字符元素就追加多少字符元素,并不会用'\0'去弥补空缺。

strncmp

strcmp - C++ Reference (cplusplus.com)

  • 比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。 

>0

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abczef";char arr2[] = "abcqw";int ret = strncmp(arr1, arr2, 4);printf("%s\n", ret);return 0;
}

<0

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abcdef";char arr2[] = "abcqw";int ret = strncmp(arr1, arr2, 4);printf("%s\n", ret);return 0;
}

=0

#include<stdio.h>
int main()
{char arr1[] = "abcdef";char arr2[] = "abcdef";int ret = strncmp(arr1, arr2, 3);printf("%s\n", ret);return 0;
}

这里留下几个任务就是动动小手我们写一写在原来的基础上模拟实现strnpy strncat strncmp,下篇博文讲。🙂🙂🙂🙂🙂

总结

  • 作用会包含'\0'的字符串函数:strcpy strcat strncat
  • 不会作用会包含'\0'的字符串函数:strncpy 
  • 需要作用包括\0的字符串函数像追加/拷贝,会覆盖掉目的地空间字符串的\0,必须带上\0
  • 不需要是因为即便拷贝过去,目的地函数里原本就有多余的\0,不需要拷贝例如strnpy
  • 各个字符串函数的模拟实现要掌握

✔✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎指正!下篇博文我们继续字符函数。🆗

当然今天有很开心的事情就是,以为我不能考英语四级了,结果候补有位置了,真的很开心!当然也提醒大家抢四级报名不要慌,也不要一直刷新和操作,这样会和我一样强制下线的┭┮﹏┭┮

代码------→【gitee:唐棣棣 (TSQXG) - Gitee.com】

联系------→【邮箱:2784139418@qq.com】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/142304.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

echarts添加点击事件

实现效果&#xff1a;点击图表&#xff0c;弹出该数据下对应得详情 官方文档&#xff1a; 封装的图表组件中&#xff1a; 点击获取点击得对象&#xff0c;进而将需要的参数传给父组件&#xff0c;在父组件中再去请求接口获取更多信息 this.chart.on(click, (params)> {th…

Docker 安装Redis(集群)

3主3从redis集群配置 1、新建6个docker容器 redis 实例 docker run -d --name redis-node-1 --net host --privilegedtrue -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381 docker run -d --name redis-node-2 --ne…

聚焦云原生安全|如何为5G边缘云和工业互联网应用筑牢安全防线

9月22日&#xff0c;2023年中国信息通信业发展高层论坛5G工业互联网分论坛在北京顺利举办。 作为国内云原生安全领导厂商&#xff0c;安全狗受邀出席此次活动。 据悉&#xff0c;中国信息通信业发展高层论坛是致力于研究信息通信业发展新问题、新趋势&#xff0c;推动信息通信…

使用vite插件进行低代码平台自定义开发(手机版自定义范例)

前言 Youtube上的前端网红「Theo」在React文档仓库发起了一个Pull request&#xff0c;号召React文档不要再默认推荐CRA(create react app)&#xff0c;而是应该将Vite作为构建应用的首选。 vite的影响力已经从vue蔓延到了react&#xff0c;可见在前端工程化开发中&#xff0c…

如何使用ArcGIS Pro将等高线转DEM

通常情况下&#xff0c;我们拿到的等高线数据一般都是CAD格式&#xff0c;如果要制作三维地形模型&#xff0c;使用栅格格式的DEM数据是更好的选择&#xff0c;这里就为大家介绍一下如何使用ArcGIS Pro将等高线转DEM&#xff0c;希望能对你有所帮助。 创建TIN 在工具箱中选择“…

如何构建基于大模型的App

ChatGPT 的出现让大模型再一次成为业界的关注热点&#xff0c;然而&#xff0c;并不是每个组织都要去训练及生成大模型的&#xff0c;而且各个组织的技术积累和计算资源也不太允许这样去做。更多的时候&#xff0c; 我们还是基于大模型开发业务应用。所谓智能原生&#xff08;A…

SpringMVC-请求与相应

一、环境准备 <dependencies><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope> //确定范围避免与tomcat冲突</de…

vivado乘法器IP核进行无符号与有符号数相乘问题的验证

本文验证乘法器IP核Multiplier进行无符号(unsigned)与有符号数(signed)相乘的正确性&#xff0c;其中也遇到了一些问题&#xff0c;做此记录。 配套工程&#xff1a;https://download.csdn.net/download/weixin_48412658/88354179 文章目录 问题的讨论验证过程IP核配置例化乘…

python+nodejs+php+springboot+vue 导师双选系统

为了直观显示系统的功能&#xff0c;运用用例图这样的工具显示分析的结果。分析的导师功能如下。导师管理导师选择信息&#xff0c;管理项目&#xff0c;管理项目提交并对学员提交的项目进行指导。 为了直观显示系统的功能&#xff0c;运用用例图这样的工具显示分析的结果。分析…

win11、win10使用python代码打开和关闭wifi热点的正确方法

问题一 win10、win11&#xff0c;可以在任务栏的WIFI图标启动移动热点&#xff0c;但是无法设置SSID和密码。在网上搜索好久&#xff0c;无解。 万能的网络解决不了&#xff0c;只能自己动手解决了。 问题二 我当前的WiFi驱动程序不支持承载网络&#xff0c;如果我输入netsh…

若依cloud -【 100 ~ 103 】

100 分布式日志介绍 | RuoYi 分布式日志就相当于把日志存储在不同的设备上面。比如若依项目中有ruoyi-modules-file、ruoyi-modules-gen、ruoyi-modules-job、ruoyi-modules-system四个应用&#xff0c;每个应用都部署在单独的一台机器里边&#xff0c;应用对应的日志的也单独存…

【MySQL基础 | 中秋特辑】多表查询详细总结

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【MySQL学习专栏】&#x1f388; 本专栏旨在分享学习MySQL的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 目录 一、多表…

SI3262:国产NFC+MCU+防水触摸按键三合一SoC芯片

目录 SI3262简介特点结构框图芯片特性 SI3262简介 Si3262是高度集成ACD低功耗MCUNFC15通道防水触摸按键的SoC芯片。 其MCU模块具有低功耗、Low Pin Count、宽电压工作范围&#xff0c;集成了13/14/15/16位精度的ADC、LVD、UART、SPI、I2C、TIMER、WUP、IWDG、RTC、TSC等丰富的…

基于微信小程序的婚庆婚纱摄影预约平台(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

nodejs+vue 医院病历管理系统

系统使用权限分别包括管理员、病人和医生&#xff0c;其中管理员拥有着最大的权限&#xff0c;同时管理员的功能模块也是最多的&#xff0c;管理员可以对系统上所有信息进行管理。用户可以修改个人信息&#xff0c;对医院病历信息进行查询&#xff0c;对住院信息进行添加、修改…

Learn Prompt-Prompt 高级技巧:思维链 Chain of Thought Prompting

Jason Wei等作者对思维链的定义是一系列的中间推理步骤&#xff08; a series of intermediate reasoning steps &#xff09;。目的是为了提高大型语言模型&#xff08;LLM&#xff09;进行复杂推理的能力。 思维链通常是伴随着算术&#xff0c;常识和符号推理等复杂推理任务出…

7实体与值对象 #

本系列包含以下文章&#xff1a; DDD入门DDD概念大白话战略设计代码工程结构请求处理流程聚合根与资源库实体与值对象&#xff08;本文&#xff09;应用服务与领域服务领域事件CQRS 案例项目介绍 # 既然DDD是“领域”驱动&#xff0c;那么我们便不能抛开业务而只讲技术&…

省时省力!推荐几款国内高效办公软件

随着信息技术的快速发展&#xff0c;办公室的工作越来越依赖于电脑和互联网。而高效的办公软件也成为了办公室必不可少的工具。今天我们来分享一些国内的高效办公软件品牌&#xff0c;希望对大家有所帮助。 1、J2L3x J2L3x 是一款专为团队通信而设计的工具&#xff0c;旨在将…

C语言自定义类型讲解:结构体,枚举,联合(2)

&#x1f435;本篇文章将会对位段、枚举和联合的相关知识进行讲解 1. 位段&#x1f4da; 1.1 什么是位段 位段的声明和结构体类似&#xff0c;但是有两点不同&#xff1a; 1.位段的成员必须是int&#xff0c;unsigned int&#xff0c;signed int (C99之后也可以是其他成员&am…

【C语言】通讯录

目录 一、关于通讯录 二、代码逻辑 三、通讯录实现 1.菜单设计 2.逻辑主要功能设计 3.增加联系人功能实现 4.显示全部联系人信息 5.删除联系人 6.查找联系人 7.修改联系人信息 8.对联系人进行排序 9.一键清空所有联系人 四、完整源码 test.c contact.c contact.…