C语言_通讯录

“我若成佛,天下无魔;我若成魔,佛奈我何。”

“小爷是魔,那又如何?”

下面我和一起来攻克通讯录的难关!!

在这里插入图片描述

明确通讯录的基本结构

实现一个通讯录:

人的信息:
名字+年龄+性别+电话+地址

实现通讯录的基本步骤

假设
1.存放100个人的信息
2.增加联系人
3.删除指定联系人
4.查找联系人
5.修改联系人
6.排序
7.显示联系人

首先我们要先创建三个文件

三个文件的功能如下:

  • test.c -> 测试功能
  • contact.c->通讯录相关的实现
  • contact.h->通讯录相关的声明

test.c(大体的逻辑框架)

测试通讯录基本的框架

#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"

void menu()
{
	printf("****************************************************\n");
	printf("**********   1.add增           2.del删      ********\n");
	printf("**********   3.search查        4.modify改   ********\n");
	printf("**********   5.shor显示        6.sort排序   ********\n");
	printf("**********           0.exit退出             ********\n");
	printf("****************************************************\n");
}
int main()
{
	//使用do while结构打印一个菜单
	int input = 0;
	//创建通讯录
	Contact con;//通讯录(这个con就是通讯录)
	//初始化通讯录
    InitContact(&con);//要用指针接收
    //这里为什么要传地址(有两点原因)
    //1.结构体传参传地址更加准确
    //2.要修改这个通讯录就必须传的是地址(参考修改一个整数)
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
			//由于枚举常量实际上就是整数,因此可以直接将input的值与枚举常量进行比较
		case ADD:
			//增加人的信息
			AddContact(&con);
			break;
		case DEL:
			//删除
			DelContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case PRINT:
			PrintContact(&con);//打印,不会改变con
			break;
		case EXIT:
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

contact.h(类型的声明)

1.用结构体定义人的信息

//类型的声明
typedef struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];

}PeoInfo;

2.封装一个通讯录

在contact.h中声明函数

void InitContact(Contact* pc);//前面取的是con的地址 所以用pc指针接收

在contact.c中实现此函数

typedef struct Contact
{
	PeoInfo data[MAX];//存放添加进来的人的信息,假设开辟100个空间
	int sz;//记录当前通讯录中有效信息的个数
}Contact;

这里说明通讯录中既有人的信息,又有人的个数

3.初始化通讯录

void InitContact(Contact* pc)
{
	pc->sz = 0;
	//memset();
	memset(pc->data, 0, sizeof(pc->data));//将pc指向的数组初始化为0
	//也就是将开辟的100个空间全部都初始化为0
}

补充一个知识:memset 的使用

memset用于将一段内存区域的每个字节都设置为指定的值

void *memset(void *s, int c, size_t n);
  • s:指向要填充的内存块的指针
  • c:要设置的值,该值以 int 类型传递,但在填充内存时会被转换为 unsigned char 类型。(例如初始化为几)
  • n:要填充的字节数

返回值指向内存块s指针

注意:
在contact.h中引入头文件#include<string.h>

4.增加联系人到通讯录中

声明:

void AddContact(Contact* pc);

实现:

void AddContact(Contact* pc)
{
	if (pc->sz == MAX)
	{
		printf("通讯录已满,无法添加\n");
		return;
	}
	//增加一个人的信息
	printf("请输入名字:>");
	scanf("%s", pc->data[pc->sz].name);
	//这里指的是将一个人的信息存放到date数组下标为sz的数组里面(因为刚开始pc指向的是date数组)
	//数组名不需要取地址
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->sz].age));
	//这里的age是一个整形变量所以需要取地址
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);
	
	pc->sz++;
	printf("增加成功\n");
}
  • 这里详细解释一下pc->data[pc->sz].name的意思

pc->data[pc->sz].name: pc 是指向Contact结构体的指针pc->data 访问的是Contact结构体中的data数组,pc->sz 表示当前已存储的联系人数量pc->data[pc->sz] 就是data数组中第pc->sz个元素(即当前要添加的联系人信息),pc->data[pc->sz].name 则是该联系人的姓名数组。由于数组名本身就代表数组首元素的地址,所以不需要取地址符 &

  • pc->sz++ 又是什么意思呢

pc->sz++ 是一个自增操作,它的作用是将Contact结构体中的sz成员的值加 1。在添加新联系人信息时,sz 表示当前已存储的联系人数量。当成功添加一个新联系人后,已存储的联系人数量就会增加 1,所以需要将sz的值加 1。这样,下次再添加联系人时,就会将新联系人的信息存储到 data 数组的下一个位置。

5.打印联系人信息

声明:

void ShorContact(const Contact* pc);

实现:

void ShorContact(const Contact* pc)
{
	assert(pc);//断言一下,以防空指针
	int i = 0;
	printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");
	//加负号表示左对齐
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,
			                                   pc->data[i].age, 
		                                       pc->data[i].sex,
			                                   pc->data[i].tele,
			                                   pc->data[i].addr);
	}
}

注意:

  • pc->sz 表示当前Contact 结构体中已经存储的联系人数量
  • for 循环的条件i < pc->sz确保循环会遍历data数组中所有已存储的联系人信息。每次循环结束后,i 的值会加 1,直到i等于 pc->sz 时,循环停止
  • 这里的格式化字符串"%20s\t%3d\t%5s\t%12s\t%30s\n"详细解释如下:
    • %20s:表示以字符串形式输出,并且该字符串的最小宽度为 20 个字符。如果实际字符串长度小于 20,则会在左边补齐空格,用于输出联系人的姓名。
    • %5d:表示以十进制整数形式输出,最小宽度为 5 个字符,用于输出联系人的年龄。
    • %5s:表示以字符串形式输出,最小宽度为 5 个字符,用于输出联系人的性别。
    • %12s:表示以字符串形式输出,最小宽度为 12 个字符,用于输出联系人的电话号码。
    • %30s:表示以字符串形式输出,最小宽度为 30 个字符,用于输出联系人的地址。
    • \t 是制表符,用于在输出中分隔不同的信息字段,使输出更加整齐。
    • \n 是换行符,用于在输出完一个联系人的信息后换行,方便查看。

最后再详细说明一下作用:

  • pc->data 等价于 (*pc).data,表示访问pc所指向的Contact结构体中的data数组。
  • data[i] 表示 data 数组中的第i个元素,由于 data 数组的元素类型是 PeoInfo,所以 data[i] 就是一个 PeoInfo 结构体变量,代表第i个联系人的详细信息

6.删除联系人的信息

声明:

void DelContact(Contact* pc);

实现:

static int FindByName(Contact* pc, char name[])
{
	assert(pc);
	int i = 0;//i 将作为循环计数器,用于访问 pc->data 数组中的每个元素
	for (i = 0; i < pc->sz; i++)//遍历通讯录中的所有联系人
	{
		if (0 == strcmp(pc->data[i].name, name))//比较当前联系人的名字与要查找的名字是否相同
		{
			return i;//返回下标
		}
	}
	return -1;//没找到该联系人就返回-1
}
void DelContact(Contact* pc)
{
	char name[MAX_NAME] = { 0 };//定义一个字符数组 name,用于存储用户输入的要删除的联系人名字
	assert(pc);//断言 确保传入的 Contact* pc 指针是有效的
	int i = 0;
	if (pc->sz == 0)//检查通讯录是否为空
	{
		printf("通讯录为空,没有信息可以删除\n");
		return;
	}
	printf("请输入要删除人的名字:>");
	scanf("%s", name);
	//删除
	//1.查找
	int pos = FindByName(pc, name);
	//调用 FindByName 函数,在 Contact 结构体中查找指定姓名的联系人,并将返回的下标存储在 pos 变量中
	if (pos == -1)//检查 pos 是否为 -1。如果为 -1,说明未找到要删除的联系人
	{
		printf("要删除的人不存在\n");
		return;
	}
	//2.删除
	for (i = pos; i < pc->sz - 1; i++)
	//当找到要删除的联系人后,需要将该联系人后面的所有联系人信息依次向前移动一位,覆盖掉要删除的联系人信息。
	{
		pc->data[i] = pc->data[i + 1];
		//从 pos 位置开始,将 data 数组中 i + 1 位置的元素赋值给 i 位置,直到遍历到倒数第二个元素
	}
	pc->sz--;
	//删除联系人后,已存储的联系人数量减少 1,因此将 pc->sz 的值减 1
	printf("删除成功\n");
}

对上面这段代码进行详细分析:

  • 函数 FindByName功能概述:
    FindByName 函数的主要功能是在Contact结构体所存储的联系人信息中,根据输入的姓名查找对应的联系人,并返回该联系人在data数组中的下标。如果未找到匹配的联系人,则返回 -1
  • DelContact 函数
    DelContact 函数的主要功能是从Contact结构体所存储的联系人信息中删除指定姓名的联系人。

注意:
这里for (i = pos; i < pc->sz - 1; i++)为什么要sz-1?
原因:
pc->sz 表示当前通讯录中联系人的数量,因此最后一个联系人的索引是 pc->sz - 1(也就是下标)如果不减一的话就会造成越界访问

7.查找指定联系人

声明:

void SearchContact(Contact* pc);

实现:

void SearchContact(Contact* pc)
{
	assert(pc);
	char name[MAX_NAME] = { 0 };//MAX_NAME 是一个常量,表示名字的最大长度并将数组初始化为全 0
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	//1.查找
	int pos = FindByName(pc, name);
	//FindByName 函数会返回该联系人在 data 数组中的下标,如果未找到则返回 -1。将返回值存储在 pos 变量中
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;//找不到就返回
	}
	//2.打印
	else
	{
		printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");
		//找到了就打印
		printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[pos].name,
			pc->data[pos].age,
			pc->data[pos].sex,
			pc->data[pos].tele,
			pc->data[pos].addr);

	}
}

上述代码的整体基本逻辑如下:

  1. 输入名字:
    提示用户输入要查找的联系人名字,并将其存储到name数组中。

  2. 查找联系人:
    调用FindByName函数,根据名字在通讯录中查找联系人。
    如果未找到,输出提示信息并结束函数。

  3. 打印联系人信息:
    如果找到联系人,格式化输出其详细信息(名字、年龄、性别、电话、地址)。

8.修改指定联系人

声明:

void ModifyContact(Contact* pc);

实现:

void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[MAX_NAME] = { 0 };
	printf("请输入要修改人的名字:>");
	scanf("%s", name);
	//1.查找
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要修改的人不存在\n");
		return;//找不到就返回
	}
	printf("要修改人的信息已查找到,接下来请开始修改\n");
	//2.修改
		printf("请输入名字:>");
		scanf("%s", pc->data[pos].name);
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[pos].age));
		printf("请输入性别:>");
		scanf("%s", pc->data[pos].sex);
		printf("请输入电话:>");
		scanf("%s", pc->data[pos].tele);
		printf("请输入地址:>");
		scanf("%s", pc->data[pos].addr);

		printf("修改成功\n");
	
}

基本逻辑

  1. 输入名字:
    提示用户输入要修改的联系人名字,并将其存储到 name 数组中。

  2. 查找联系人:
    调用 FindByName 函数,根据名字在通讯录中查找联系人。
    如果未找到,输出提示信息并结束函数。

  3. 修改联系人信息:
    如果找到联系人,提示用户依次输入新的名字、年龄、性别、电话和地址。
    将用户输入的信息更新到通讯录中。

  4. 提示修改成功:
    输出提示信息,表示联系人信息已成功修改。

9.排序通讯录的信息(可以按照名字排序也可以按照年龄来排序)

这里举的例子是按照名字来排序

int cmp_peo_by_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
	//将 void* 指针转换为 PeoInfo* 类型,然后访问 name 字段
}

//按照名字来排序
void SortContact(Contact* pc)//Contact* pc: 指向通讯录结构体的指针,包含联系人数据
{
	assert(pc);
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_peo_by_name);
	//pc->data: 指向联系人数组的起始地址。
    //pc->sz: 联系人数组中元素的数量。
    //sizeof(PeoInfo): 每个联系人结构体的大小。
    //cmp_peo_by_name: 比较函数,用于定义排序规则。
	printf("排序成功\n");
}

补充知识
1.qsort 函数
qsort 是 C 标准库(<stdlib.h>)中提供的通用排序函数,用于对数组进行排序。

  • 函数原型:
void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*));
  • 参数:

    • base: 指向数组的起始地址。
    • nmemb: 数组中元素的数量。
    • size: 每个元素的大小(以字节为单位)。
    • compar: 比较函数,用于定义排序规则。
  • 比较函数:
    比较函数接收两个 const void* 类型的参数,返回一个整数:

    • 负数:第一个参数小于第二个参数。
    • 0:两个参数相等。
    • 正数:第一个参数大于第二个参数。
  1. strcmp 函数
    strcmp 是 C 标准库(<string.h>)中提供的字符串比较函数,用于比较两个字符串的字典序。
  • 函数原型:
int strcmp(const char* str1, const char* str2);
  • 参数:

    • str1: 第一个字符串。
    • str2: 第二个字符串。
  • 返回值:

    • 负数:str1 小于 str2。
    • 0:str1 等于 str2。
    • 正数:str1 大于 str2。
  • 比较规则:

    • 按字符的 ASCII 值逐个比较。
    • 比较过程在遇到第一个不相等的字符或字符串结束符 \0 时停止。

10.contact.h的整体声明如下:

#pragma once
#include<string.h>
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#define MAX 100

# define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30


//类型的声明
typedef struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];

}PeoInfo;

//通讯录
typedef struct Contact
{
	PeoInfo data[MAX];//存放添加进来的人的信息,假设开辟100个空间
	int sz;//记录当前通讯录中有效信息的个数
}Contact;



//初始化通讯录
void InitContact(Contact* pc);//前面取的是con的地址 所以用pc指针接收


//增加联系人
void AddContact(Contact* pc);

//打印联系人信息
void ShowContact(const Contact* pc);

//删除联系人的信息
void DelContact(Contact* pc);

//查找指定联系人
void SearchContact(Contact* pc);

//修改指定联系人
void ModifyContact(Contact* pc);

//排序通讯录的信息
void SortContact(Contact* pc);

在这里插入图片描述

最后大体的整合一下就完成啦!
注意:
本章只是对通讯录的静态表示方式噢~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32 Flash详解教程文章

目录 Flash基本概念理解 Flash编程接口FPEC Flash擦除/写入流程图 Flash选项字节基本概念理解 Flash电子签名 函数读取地址下存放的数据 Flash的数据处理限制部分 编写不易&#xff0c;请勿搬运&#xff0c;感谢理解&#xff01;&#xff01;&#xff01; Flash基本概念…

Flutter项目试水

1基本介绍 本文章在构建您的第一个 Flutter 应用指导下进行实践 可作为项目实践的辅助参考资料 Flutter 是 Google 的界面工具包&#xff0c;用于通过单一代码库针对移动设备、Web 和桌面设备构建应用。在此 Codelab 中&#xff0c;您将构建以下 Flutter 应用。 该应用可以…

第六篇:数字逻辑的“矩阵革命”——域控制器中的组合电路设计

副标题 &#xff1a;用卡诺图破解车身域控制器的逻辑迷宫&#xff0c;揭秘华为DriveONE的“数字特工” ▍ 开篇&#xff1a;黑客帝国世界观映射 > "Welcome to the Real World." —— Morpheus > 在数字逻辑的世界里&#xff0c;组合电路就是构建Matr…

成为高能量体质:从身体神庙到精神圣殿的修炼之路

清晨五点&#xff0c;当城市还在沉睡&#xff0c;瑜伽垫上的汗水已经折射出第一缕阳光。这不是苦行僧的自虐&#xff0c;而是高能量体质者的日常仪式。在这个能量稀缺的时代&#xff0c;如何把自己修炼成一座小型核电站&#xff1f;答案就藏在身体的每个细胞里。 一、能量管理…

从大规模恶意攻击 DeepSeek 事件看 AI 创新隐忧:安全可观测体系建设刻不容缓

作者&#xff1a;羿莉&#xff08;萧羿&#xff09; 全球出圈的中国大模型 DeepSeek 作为一款革命性的大型语言模型&#xff0c;以其卓越的自然语言处理能力和创新性成本控制引领行业前沿。该模型不仅在性能上媲美 OpenAI-o1&#xff0c;而且在推理模型的成本优化上实现了突破…

低成本+高性能+超灵活!Deepseek 671B+Milvus重新定义知识库搭建

“老板说&#xff0c;这个项目得上Deepseek,还得再做个知识库...” 还有哪个开发者&#xff0c;最近没听到这样的抱怨&#xff1f; Deepseek爆火&#xff0c;推理端的智能提速&#xff0c;算力成本急剧下降&#xff0c;让不少原本不想用大模型&#xff0c;用不起大模型的企业&a…

CSS 实现下拉菜单效果实例解析

1. 引言 在 Web 开发过程中&#xff0c;下拉菜单是一种常见且十分实用的交互组件。很多前端教程都提供过简单的下拉菜单示例&#xff0c;本文将以一个简洁的实例为出发点&#xff0c;从 HTML 结构、CSS 样式以及整体交互逻辑三个层面进行详细解析&#xff0c;帮助大家理解纯 C…

VSCode中出现“#include错误,请更新includePath“问题,解决方法

1、出现的问题 在编写C程序时&#xff0c;想引用头文件但是出现如下提示&#xff1a; &#xff08;1&#xff09;首先检查要引用的头文件是否存在&#xff0c;位于哪里。 &#xff08;2&#xff09;如果头文件存在&#xff0c;在编译时提醒VSCode终端中"#include错误&am…

【RabbitMQ的监听器容器Simple和Direct】 实现和场景区别

在Spring Boot中&#xff0c;RabbitMQ的两种监听器容器&#xff08;SimpleMessageListenerContainer和DirectMessageListenerContainer&#xff09;在实现机制和使用场景上有显著差异。以下是它们的核心区别、配置方式及最佳实践&#xff1a; Simple类型 Direct类型 一、核心…

yolo11自训练极简教程 - 训练侦测

去年我处理过的最后一个版本是yolov10.新年再次着手处理视频识别的工作。发现自清华的v10之后&#xff0c;去年下半年v11再次发布了。国内访问github有些问题&#xff0c;但是yolo网站是可以方便访问的&#xff1a; Train - Ultralytics YOLO Docs 国外的知识库做的很棒&…

大数据学习之SparkStreaming、PB级百战出行网约车项目一

一.SparkStreaming 163.SparkStreaming概述 Spark Streaming is an extension of the core Spark API that enables scalable, high-throughput, fault-tolerant stream processing of live data streams. Spark Streaming 是核心 Spark API 的扩展&#xff0c;支持实时数据…

day5QT套接字通信

Widget.cpp #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);objtimer new QTimer (this);//连接定时器的timeout信号到启动的槽函数//connect(objtimer,&…

【免费】2007-2020年各省医疗卫生支出数据

2007-2020年各省医疗卫生支出数据 1、时间&#xff1a;2007-2020年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区名称、年份、医疗卫生支出 4、范围&#xff1a;31省 5、指标说明&#xff1a;地方财政医疗卫生支出是指地方ZF从其财政预…

本地基于GGUF部署的DeepSeek实现轻量级调优之二:检索增强生成(RAG)

前文&#xff0c;我们在本地windows电脑基于GGUF文件&#xff0c;部署了DeepSeek R1 1.5B模型&#xff0c;如果想在离线模式下加载本地的DeepSeek模型自行对进行训练时&#xff0c;是不能直接使用GGUF文件进行训练的&#xff0c;但是可以对模型进行微调&#xff0c;以下说的是第…

16vue3实战-----动态路由

16vue3实战-----动态路由 1.思路2.实现2.1创建所有的vue组件2.2创建所有的路由对象文件(与上述中的vue文件一一对应)2.3动态加载所有的路由对象文件2.4根据菜单动态映射正确的路由2.5解决main页面刷新的问题2.6解决main的第一个页面匹配显示的问题2.7根据path匹配menu 1.思路 …

WPS如何接入DeepSeek(通过JS宏调用)

WPS如何接入DeepSeek 一、文本扩写二、校对三、翻译 本文介绍如何通过 WPS JS宏调用 DeepSeek 大模型&#xff0c;实现自动化文本扩写、校对和翻译等功能。 一、文本扩写 1、随便打开一个word文档&#xff0c;点击工具栏“工具”。 2、点击“开发工具”。 3、点击“查看代码”…

前端快速生成接口方法

大家好&#xff0c;我是苏麟&#xff0c;今天聊一下OpenApi。 官网 &#xff1a; umijs/openapi - npm 安装命令 npm i --save-dev umijs/openapi 在根目录&#xff08;项目目录下&#xff09;创建文件 openapi.config.js import { generateService } from umijs/openapi// 自…

云消息队列 ApsaraMQ Serverless 演进:高弹性低成本、更稳定更安全、智能化免运维

如今&#xff0c;消息队列已成为分布式架构中不可或缺的关键服务&#xff0c;为电商、物联网、游戏和教育等行业&#xff0c;提供了异步解耦、集成、高性能和高可靠的核心价值。 过去一年&#xff0c;我们发布了云消息队列 ApsaraMQ 全系列产品 Serverless 化&#xff0c;面向…

Spring依赖注入方式

写在前面&#xff1a;大家好&#xff01;我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正&#xff0c;感谢大家的不吝赐教。我的唯一博客更新地址是&#xff1a;https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油&#xff0c;冲鸭&#x…

Mysql索引失效的场景

对索引列使用函数或表达式&#xff0c;或参与计算&#xff08;优化方法&#xff1a;将计算移到条件右侧&#xff1a;&#xff09;例 优化 对索引列进行隐式类型转换&#xff0c;条件中的数据类型与索引列的数据类型不匹配&#xff0c;会进行隐式类型转换 以like 通配符开头索…