C语言期末复习指南基础篇1.C的输入输出2.C的判断—->条件语句if
语句:switch
语句:3.C的循环语句while
循环:for
循环:do…while
循环:4.C的作用域规则和函数作用域局部变量:全局变量:形式参数:函数:5.数组进阶篇1.指针什么是指针?指针和数组指针和函数2.结构体什么是结构体?简单使用结构体3.链表初始化链表,添加节点链表的基本操作遍历链表&查找节点删除指定的节点在指定位置插入节点:修改指定节点:细节总结:几种常见的排序算法:题目模式:1.读程序题:2.程序填空题:3.任务驱动编程题:就这。
按照软件学院的惯例,C语言期末考试是不直接考概念填空题和选择题的。
所以我们今天要讲的基本是贴近代码的情况:
程序从main
函数开始执行之类的概念相信已经不必多说了,所以我们先看到输入输出的一些考察点:
举例,通常情况下最多用到的输入函数是scanf
:
xxxxxxxxxx
61
2int main(void)
3{
4 int a;
5 scanf("%d",&a);
6}
通常考察的也就是格式控制和地址符&
,保持前面的格式控制和要存储的变量类型一致就OK了,另外,除了指针以外,变量前都是要加地址符&
的。这种情况在改错题可能会遇到,还有自己写代码的时候也不能犯错。
输出一般也就是用到printf
:
xxxxxxxxxx
61
2int main(){
3 int a = 0;
4 int* b = &a;
5 printf("The variable is %d and %d", a, *b);
6}
主要也是考察一个格式控制的问题,如果是指针记得加*
。
C语言内置的条件语句有两个:
if
switch
if
语句:一般在读程序题里考察:
xxxxxxxxxx
171
2
3int main()
4{
5 int x = 1, y = 2;
6 for (; x < 10; x++)
7 {
8 x += 2;
9 if (x > 7)
10 break;
11 if (x == 6)
12 continue;
13 y *= x;
14 }
15 printf("%d %d\n", x, y);
16 return 0;
17}
这里的输出是
9 6
首先要明确if
的工作方式:
x
1if(A)
2{
3 B;
4}
5else if (C)
6{
7 D;
8}
9else
10{
11 E;
12}
程序执行到if
的位置之后,
首先判断A
的真假:
B
,然后结束if…else
代码;C
的真假,重复上述步骤;else if
之后如果没有找到真,就执行else
里的E语句。如果有多个if
嵌套,用这样的逻辑去一层一层推导,就不会出问题。
switch
语句:xxxxxxxxxx
1
2
3int main ()
4{
5 /* 局部变量定义 */
6 char grade = 'B';
7
8 switch(grade)
9 {
10 case 'A' :
11 printf("很棒!\n" );
12 break;
13 case 'B' :
14 case 'C' :
15 printf("做得好\n" );
16 break;
17 case 'D' :
18 printf("您通过了\n" );
19 break;
20 case 'F' :
21 printf("最好再试一下\n" );
22 break;
23 default :
24 printf("无效的成绩\n" );
25 }
26 printf("您的成绩是 %c\n", grade );
27
28 return 0;
29}
switch
更像是一个特殊化的if
,用于简化只需要判断某一个变量的值的情况,需要注意的是,正常情况下switch
的每一个case
都需要以break
结束,否则无法跳出,而default
段可以有效的防止错误的变量造成的无法跳出switch
。
如果没有写break
语句,执行完当前case
之后,将继续执行下一个case
代码段直到遇到break
。
C当中主要有三个基本的循环语句:
while
循环for
循环do...while
循环while
循环:xxxxxxxxxx
41while(condition)
2{
3 statement(s);
4}
condition
是判断语句,和if
里那个判断语句一样,为真(1/true)
则继续,为假(0/false)
则终止,大括号内的语句也就是每次循环要执行的内容。
举个例子:
x
1
2
3int main ()
4{
5 /* 局部变量定义 */
6 int a = 10;
7
8 /* while 循环执行 */
9 while( a < 20 )
10 {
11 printf("a 的值: %d\n", a);
12 a++;
13 }
14 return 0;
15}
要注意的点是,while
结束的时候的a
值是多少?
尽管程序打印的a
是到19就没有了,但是最后循环结束的a
值应该是20
,也就是刚好使得判断条件为假的值。
分析循环类的代码执行,最好分析出每次循环的变量的情况,避免出错。
for
循环:for
循环其实和while循环大差不差,也就是多了几个更直观的表示方式:
xxxxxxxxxx
41for ( init; condition; increment )
2{
3 statement(s);
4}
init
是循环开始前(刚执行到for
语句时)要执行的语句,比如赋初值之类;condition
还是判断语句,和while
循环里那个一样;increment
就是每次循环固定要执行的语句,比如大家经常见到的i++
,目的是增强可读性,作用和把这个语句直接放进大括号最后一行没有区别。
do…while
循环:do...while
循环也是一个特殊版的while
循环:
xxxxxxxxxx
51do
2{
3 statement(s);
4
5}while( condition );
它的特点是不管条件如何,第一次执行到这段代码的时候一定会执行一次statement
,因为它是先执行,再判断。也就是说,不管我们condition
是否为真,statement
都会至少执行一次。
例如:
xxxxxxxxxx
81int main(void)
2{
3 int a = 10;
4 do
5 {
6 printf("%d", a);
7 } while (a > 10);
8}
可以看到,a
是不满足while
语句中的条件的,但a
仍然会被打印一次。
C语言的作用域,主要是对三个概念的理解:
局部变量
全局变量
形式参数
在某个函数或块的内部声明的变量称为局部变量
。
x
1
2
3int main(void)
4{
5 /* 局部变量声明 */
6 int a, b;
7 int c;
8
9 /* 实际初始化 */
10 a = 10;
11 b = 20;
12 c = a + b;
13
14 printf("value of a = %d, b = %d and c = %d\n", a, b, c);
15
16 return 0;
17}
需要记住,在我们看到int、char
之类的变量声明语句时,就已经限定它的作用范围。在这里,所有的变量 a
、b
和 c
是 main()
函数的局部变量,如果有其他的函数存在,是不能直接使用这里的a
b
c
的。
如果觉得自己搞不清楚全局变量的,记住,放在函数外面的,也就是没有放在大括号里的就是全局变量,这个变量是大家都可以使用的。
*注意:如果在函数里有和全局变量同名的局部变量定义,在函数内以局部变量优先。
x
1
2
3/* 全局变量声明 */
4int g;
5
6int main()
7{
8 /* 局部变量声明 */
9 int a, b;
10
11 /* 实际初始化 */
12 a = 10;
13 b = 20;
14 g = a + b;
15
16 printf("value of a = %d, b = %d and g = %d\n", a, b, g);
17 //和全局变量重名的局部变量时局部变量优先
18 int g = 100;
19 printf("%d\n", g);
20 return 0;
21}
输出如下:
value of a = 10, b = 20 and g = 30
100
形式参数本身起一个传递的作用,它的名称只对使用它的函数有意义。形式参数控制传入的参数的类型和数目,它在函数内可以被认为是一个局部变量,它的值由传入的变量确定并与传入变量的名称无关。
x
1
2
3/* 全局变量声明 */
4int a = 20;
5
6int sum(int a, int b);
7
8int main()
9{
10 /* 在主函数中的局部变量声明 */
11 int a = 10;
12 int b = 20;
13 int c = 0;
14
15
16 printf("value of a in main() = %d\n", a);
17 c = sum(a, b);
18 printf("value of c in main() = %d\n", c);
19
20 return 0;
21}
22
23/* 添加两个整数的函数 */
24int sum(int a, int b)
25{
26 printf("value of a in sum() = %d\n", a);
27 printf("value of b in sum() = %d\n", b);
28
29 return a + b;
30}
value of a in main() = 10
value of a in sum() = 10
value of b in sum() = 20
value of c in main() = 30
理解了前面关于作用域的意义之后,函数的重点也就是学会声明和调用函数。
*声明需要在调用之前。
声明调用举例:
x
1/*返回的值类型 函数名(参数1,参数2……)
2{
3 要执行的代码;
4 返回值;
5}*/
6
7//函数声明
8int sum_ab(int a, int b);
9
10int main()
11{
12 int i = 10, j = 9;
13 //调用:
14 int new = sum_ab(i, j); //(这里i,j的类型要和声明的参数类型一致)
15 printf("%d", new);
16}
17
18//函数定义
19int sum_ab(int a, int b)
20{
21 sum = a + b;
22 return sum;
23}
return
的类型一定要和函数定义时一致,否则会出错。
在C语言中,当我们需要用一个变量存储多个数据的时候,我们就需要用到数组
。
数组可以存储一个固定大小的相同类型元素的顺序集合。
数组的声明很容易:
xxxxxxxxxx
61//比如这里声明一个整形的数组
2int main(void){
3 int a[10] = {0};
4 //二维数组
5 int b[10][10] = {0};
6}
C语言中数组的声明必须确定数组长度,并且长度在声明之后不能再改变,数组的内容可以用大括号{}
来表示,各元素用逗号,
隔开。
数组容易考察的点:
数组按照下标索引来访问内容,起始的元素为下标为0
。
a[i]
需要注意:下标i
不能超过数组长度-1
(因为是从0开始),否则将会出错。
尽管我们在定义数组的时候直接把长度10
填进了中括号内,但它只代表长度,a[10]
是不存在的。
而如果要将一维数组传递给函数,可以有三种形式参数:
(int *param)
指针(int param[10])
数组(int param[])
首地址二维数组的传入则相对复杂一点,这里不多讲解。
在C语言中,每一个变量都有一个内存区域,每一个内存区域都定义了可使用 &
运算符访问的地址,它表示了在内存中的一个地址。
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,我们必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
type *var-name;
因为指针存储的是地址,所以要访问指针变量指向的内容要用取值符*
。
其实平时我们经常见到指针:
scanf
函数:
xxxxxxxxxx
51int main(void)
2{
3 int a;
4 scanf("%d", &a);
5}
这里的&a
就是一个地址,可以存储到一个指针变量里。
所以我们也可以这样写代码:
xxxxxxxxxx
51int main(void)
2{
3 int *a;
4 scanf("%d", a);
5}
效果是一样的,只不过要访问输入的值要加个*
。
定义指针的时候,良好的习惯是对其进行初始化,对于指针来说,初始化的值应为NULL
或者一个变量的地址:
xxxxxxxxxx
91int main(void)
2{
3 int *a = NULL;
4 int b = 10;
5 int *c = b;
6
7 //这样的初始化指针没有指向的野指针,调用它将会出错:
8 int *d = 10;
9}
那么,说了这么多,指针有啥用呢?
首先:
任何能由数组下标来实现的操作都能用指针来完成
一个通过数组和下标实现的表达式可以等价地通过指针及其偏移量来实现:
xxxxxxxxxx
191int main(void)
2{
3 int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
4 int *p = NULL;
5 p = a; //a直接表示数组的首地址
6
7 //用下标访问数组
8 int i;
9 for (i = 0; i < 10; i++)
10 {
11 printf("%d", a[i]);
12 }
13 //用指针访问数组
14 while (p <= &a[9])
15 {
16 printf("%d", *p);
17 p++;
18 }
19}
指针数组和数组指针:
指针数组是数组,数组的每个元素都是一个指针。
数组指针是指针,指向一个数组。
首先,记住指针是指向一个内存地址的,是物理实体。
C语言的所有参数均是以“传值调用”的方式进行传递的,这意味着函数将获得参数值的一份拷贝,如果是普通变量,则是传递一个复制出的值,如果是指针变量,则是传递指针变量存储的值,也就是一个内存地址。
我们来看看这两段代码:
x
1
2
3int changevalue(int a)
4{
5 a = 100;
6 return 0;
7}
8
9int main(void)
10{
11 int a = 10;
12 changevalue(a);
13 printf("%d", a);
14}
我们直接定义整形变量a = 10
,然后调用changevalue
函数修改a
的值,再在main
中打印出a
的值。
打印的结果为:
10
a
的值并没有发生改变。
再看看这一段代码:
x
1
2
3int changevalue(int *a)
4{
5 *a = 100;
6}
7
8int main(void)
9{
10 int b = 10;
11 int *a;
12 a = &b;
13 changevalue(a);
14 printf("%d", *a);
15}
我们选用指针来操作,输出的结果:
100
为什么会有这样的差别呢?
因为当changevalue
函数被调用时,修改的变量是a
这个指针变量,也就是对指定的内存空间进行了修改,这样a
指向的值就发生了改变,尽管它们不在同一个作用域内。
这里我们就可以体会到指针的一个优点:
指针参数使得被调函数能够访问和修改主调函数中对象的值。
而这一点常常成为考点,在读程序题中可能要求给出指针变量最后的值,考查我们对指针在各种作用域中发生的变化。
另外,这里给出指针传递数组的例子:
一维数组:
数组的变量名可以直接表示数组的首地址,而数组在内存中顺序存储,所以我们可以以指针形式传入数组
xxxxxxxxxx
171void print_out(int *a, int length)
2{
3 int i = 0;
4 for (i = 0; i < length; i++)
5 {
6 printf("%d", *(a + i));
7 //通常这样访问也是可以的,结果完全一样
8 printf("%d", a[i]);
9 }
10}
11
12int main(void)
13{
14 int a[5] = {0, 1, 2, 3, 4};
15 int length = sizeof(a) / sizeof(a[0]);
16 print_out(a, length);
17}
缺点是必须传入数组长度,不能直接通过传入的指针确定数组的长度
二维数组:
二维数组用指针传递的条件稍显严格,需要指明列数(即第二维的上限)
其他操作和一维数组类似
x
1void print_b(int (*a)[5], int n, int m)
2{
3 int i, j;
4 for (i = 0; i < n; i++)
5 {
6 for (j = 0; j < m; j++)
7 printf("%d ", a[i][j]);
8 printf("\n");
9 }
10}
函数指针:指向的对象是函数的指针
例如:
x
1void echo(char *msg)
2{
3 printf("%s",msg);
4}
5int main(void)
6{
7 void (*p)(char*) = echo;
8 p("Hello!");
9 //等价于
10 //echo("Hello!");
11 return 0;
12}
这样可以简化函数的调用。我们也可以在函数参数中添加其他函数的函数指针,在不同的作用域内使用。
结构是 C 编程中另一种用户自定义的可用的数据类型,它允许我们存储不同类型的数据项。
比如我们想要描述一个物体的多个特征,比如一个叫Kevin的人:
我们在定义这样的一个结构体时,要遵循下面的规范:
xxxxxxxxxx
61struct tag { //这里是结构体变量类型,可以用于定义新的同类型结构体
2 member-list
3 member-list
4 member-list
5 ...
6} variable-list ;//这里是结构体名,访问这个结构体需要用到的是这个字段
实例:
xxxxxxxxxx
51struct Person{
2 int id;
3 int age;
4 char nickname[20];
5}Kevin = {1, 19, "He Jiefang"};
这里的Person
是大的类型,而Kevin
是Person
这个类型里的一个变量
为了访问结构的成员,我们使用成员访问运算符.
:
Kevin.id
Kevin.age
Kevin.nickname
使用举例:
x
1
2
3
4struct Person
5{
6 int id;
7 int age;
8 char nickname[20];
9} Kevin = {1, 19, "He Jiefang"};
10
11int main(void)
12{
13 struct Person Drunk;
14
15 Drunk.id = 2;
16 Drunk.age = 19;
17 //字符串的赋值使用strcpy函数
18 strcpy(Drunk.nickname, "DrunkCen");
19 //访问成员并输出
20 printf("Kevin's nickname is: %s\n", Kevin.nickname);
21 printf("Drunk's nickname is: %s\n", Drunk.nickname);
22}
当然,结构体也可以作为函数的参数传入,它支持常规的变量所支持的应用方式,这里就不展开细讲了。
结构体一旦和指针联系起来,我们就不得不提到链表了。
链表是一种数据结构,它和数组的主要区别在于是否只能顺序访问。
*数组是顺序存储在内存中的数据结构,特点是访问快,结构简单,我们可以直接访问它位于某一位置的数据。
链表中的每个元素我们称为节点(node)
。
它具有如下特点:
是不是感觉有点困惑?没有关系,我们接着往下看。
x
1//首先定义一个简单的结构体,作为一个节点。
2struct Node{
3 int data; //定义数据域
4 struct Node *next; //定义指针域,存储直接后继的节点信息
5};
我们可以看到,在这个节点中存在一个和这个节点的结构体类型相同的指针*next
,它将指向下一个节点。
举个例子:
xxxxxxxxxx
141//首先定义一个简单的结构体,作为一个节点。
2struct Node{
3 int data; //定义数据域
4 struct Node *next; //定义指针域,存储直接后继的节点信息
5};
6
7int main(void)
8{
9 struct Node *node1;
10 struct Node *node2;
11 //对结构体指针我们可以之间用->符号来访问结构体成员
12 node1->next = node2;
13 //像这样,我们就把node1内存储下一个节点的指针变成了node2的指针,这两个节点就连接起来了
14}
在正式创建链表之前,我们先认识一下这几个概念:
首节点主要是为了和头节点区分,头节点通常无意义,相当于数组a[10]
中的变量名a
起到的作用。
x
1struct Node
2{
3 int data; //定义数据域
4 struct Node *next; //定义指针域,存储直接后继的节点信息
5};
6
7//定义头尾节点为全局变量便于使用
8struct Node *head = NULL;
9struct Node *end = NULL;
10
11//添加新节点的函数
12void AddNewNode(int data)
13{
14 //创建一个节点,并分配内存空间
15 struct Node* temp = (struct Node*)malloc(sizeof(stuct Node));
16
17 temp->data = data; //对节点数据赋值
18 temp->next = NULL;
19
20 if (head->next == NULL)
21 head->next = temp; //如果现在一个节点也没有,直接添加到头节点后面
22 else
23 end->next = temp; //如果现在已经有节点,添加到尾节点的后面
24 end = temp; //最后保证尾节点始终指向最后一个节点
25}
其实单链表添加节点的逻辑很简单:
通过反复的调用AddNewNode
函数,我们就可以不断添加新的节点到链表中。
有了初始化好的链表之后,我们来看看与链表相关的基本操作
回顾对数组的操作,我们大部分的操作都涉及到了遍历数组这个过程。
要遍历链表很容易:
x
1struct Node* temp =(struct Node*)malloc(sizeof(struct Node));
2
3
4temp = head->next; //从首节点开始遍历
5
6while(temp != NULL)
7{
8 printf("%d", temp->data);
9 temp = temp->next;
10}
这里的temp= temp->next
就相当于数组遍历时的i++
,细微的差距是判断结束的条件temp != NULL
。
借助遍历查找节点也很容易:
xxxxxxxxxx
151struct Node *SearchNode(int data)
2{
3 struct Node *Node1 = (struct Node *)malloc(sizeof(struct Node));
4
5 Node1 = head->next; //从首节点开始遍历
6
7 while (Node1 != NULL)
8 {
9 if (Node1->data == data)
10 {
11 return Node1;
12 }
13 Node1 = Node1->next;
14 }
15}
如果要删除链表中的某个节点,是怎样的情形?
比如我们的节点连接是这样的:
如果我们要删除Node2
,只需要将Node1.next
指向Node3
,然后把Node2.next
指向NULL
, 再将Node2
释放掉就好了
x
1void DeleteNode(int data)
2{
3 struct Node *Node1 = (struct Node *)malloc(sizeof(struct Node));
4 struct Node *Node2 = (struct Node *)malloc(sizeof(struct Node));
5
6 Node1->next = head; //Node1始终在Node2之前一个节点
7 Node2 = head; //从首节点开始遍历
8
9 while (Node2 != NULL)
10 {
11 //若要删除的节点是头节点,直接更改头节点,不释放head
12 if (head->data == data)
13 {
14 head = head->next;
15 }
16 if (Node2->data == data)
17 {
18 Node1->next = Node2->next; //将Node1节点指向Node2的下一个节点
19 Node2->next = NULL; //释放Node2节点的后序节点
20 free(Node2); //释放Node2节点
21 break;
22 }
23 Node1 = Node1->next;
24 Node2 = Node2->next;
25 }
26}
了解了删除某个节点之后,我们还需要了解对应的插入节点操作
以这个图为例:
如果要插入一个Node_new
到Node1
和Node2
之间,我们需要什么样的操作?
可以很容易看到我们需要干的事情:将Node_new.next
指向Node2
,再将Node1.next
指向Node_new
x
1void insertNode(struct Node *Node_new, int data)
2{
3 struct Node *Node1 = (struct Node *)malloc(sizeof(struct Node)); //从首节点开始遍历
4
5 Node1 = head;
6
7 while (Node1 != NULL)
8 {
9 if (Node1->data == data)
10 {
11 Node_new->next = Node1->next; //将Node_new节点next指向Node2
12 Node1->next = Node_new; //将Node1节点next指向Node_new
13 break;
14 }
15 Node1 = Node1->next;
16 }
17}
增删查改,接下来要讲到的就是修改指定的节点内容了,我们仍然建立在遍历的基础上:
xxxxxxxxxx
161void changeNode(int data, int changeValue)
2{
3 struct Node *Node = (struct Node *)malloc(sizeof(struct Node));
4
5 Node = head;
6
7 while (Node != NULL)
8 {
9 if (Node->data = data)
10 {
11 Node->data = changeValue;
12 break;
13 }
14 Node = Node->next;
15 }
16}
相对比较简单,我们就不再细讲。
struct Node *Node = (struct Node *)malloc(sizeof(struct Node))
不必好奇为什么要这么写,这里的强制类型转换是为了保证内存空间分配充足避免可能的内存泄漏。(这里只要记住就够了)head
本身不直接拿来使用,而是创建一个新的指针指向它来用于遍历等操作,千万不要释放(free
)头节点!冒泡排序:
x
1
2
3int main(void)
4{
5 int a[10] = {213, 2131, 24, 54, 65, 123, 4324, 3432, 0, 19};
6 int i, j;
7 for (i = 0; i < 9; i++)
8 {
9 for (j = 0; j < 9 - i; j++)
10 {
11 if (a[j] > a[j + 1])
12 {
13 int temp = a[j];
14 a[j] = a[j + 1];
15 a[j + 1] = temp;
16 }
17 }
18 }
19}
选择排序
xxxxxxxxxx
221
2
3int main(void)
4{
5 int a[10] = {213, 2131, 24, 54, 65, 123, 4324, 3432, 0, 19};
6 int i, j;
7 for (i = 0; i < 10; i++)
8 {
9 int min = i;
10 for (j = 0; j < 10; j++)
11 {
12 if (a[j] < a[min])
13 {
14 min = j;
15 }
16 }
17 /* 交换元素 */
18 int temp = a[min];
19 a[min] = a[i];
20 a[i] = temp;
21 }
22}
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
重复第二步,直到所有元素均排序完毕。
插入排序
假设前面 n-1(其中 n>=2)个数已经是排好顺序的,现将第 n 个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。
按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序。
xxxxxxxxxx
161
2
3int main(void)
4{
5 int a[10] = {213, 2131, 24, 54, 65, 123, 4324, 3432, 0, 19};
6 int i, j;
7 for (i = 0; i < 10; i++)
8 {
9 int temp = a[i];
10 for (j = i; j > 0 && a[j - 1] > temp; j--)
11 {
12 a[j] = a[j - 1];
13 }
14 a[j] = temp;
15 }
16}
x
1
2
3int main(void)
4{
5 int x = 1, y = 2;
6 for (; x < 10; x++)
7 {
8 x += 2;
9 if (x > 7)
10 break;
11 if (x == 6)
12 continue;
13 y *= x;
14 }
15 printf("%d %d\n", x, y);
16 return 0;
17}
阅读这段程序并写出它输出的结果。
x
1int Sort(int *a, int n)
2{
3 int temp;
4 for (int i = n; i < 0; i++)
5 {
6 for (int j = 0; j < i; j++)
7 {
8 if (_________)
9 {
10 _________
11 a[j] = a[j + 1];
12 _________
13 }
14 }
15 }
16}
一般可能不会直接给出程序的具体功能,但是从程序名称可以大致确定,例如这道题是排序,我们需要通过观察已有的代码确定它使用的排序方式并正确补全。
1.用户输入一个有理数,请分离其符号部分,整数部分,以及小数部分。
2.创建数组保存周一之周日名称,用户输入一个字符串,判断是否属于已经保存的名称之一。 是则输出名称,否则输出None。
题目给出需求目标,自行设计完整代码完成需求。