在学习c语言提高总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
02-c提高06day
一、函数指针
1、如何定义函数指针?
函数名起始就是函数的入口地址
1 #define _CRT_SECURE_NO_WARNINGS
2 #include<stdio.h>
3 #include<string.h>
4 #include<stdlib.h>
5
6 //决定函数的类型应该是:函数的返回值 函数的参数列表
7
8
9 void func()
10 {
11 printf("hello world!");
12 }
13
14 //func函数名起始是代表函数的入口地址
15
16 //如何定义一个指向函数的指针?
17 int myfunc(int a, char b)
18 {
19 printf("int myfunc(int a, char b)!");
20 return 0;
21 }
22 void test01()
23 {
24 //1.定义函数类型,通过类型来定义函数指针(定义不加*,使用加/不加*都行)
25 typedef int(FUN_TYPE)(int ,char);
26 FUN_TYPE* pFunc = myfunc;
27
28 pFunc(10, 'a');
29 (*pFunc)(20, 'b');//调用的两种方式
30 myfunc(30, 'c');
31
32 //2.直接定义函数指针类型(定义加*,使用不加*)
33 typedef int(*FUNC_P)(int, char);
34 FUNC_P pFunc2 = myfunc;
35 pFunc2(20, 'd');
36
37 //函数指针指向同类型
38 //pFunc2 = func;
39
40 //3.直接定义函数指针变量,函数指针的类型是:int(*)(int, char)
41 //把指针转换为函数指针类型写法int(*pFunc3)(int, char) = int(*)(int, char) NULL;
42 int(*pFunc3)(int, char) = NULL;
43 pFunc3 = myfunc;
44 pFunc3(50, 'p');
45
46 printf("pFunc3 size:%d\n", sizeof(pFunc3));
47 }
48
49
50
51 int main(){
52
53 test01();
54
55 system("pause");
56 return EXIT_SUCCESS;
57 }
2、函数指针做函数式参数

1 #define _CRT_SECURE_NO_WARNINGS
2 #include<stdio.h>
3 #include<string.h>
4 #include<stdlib.h>
5
6 int con1(int a, int b)
7 {
8 return a + b;
9 }
10
11 int con2(int a, int b)
12 {
13 return a + b + 10;
14 }
15
16 int con3(int a, int b)
17 {
18 return a + b - 10;
19 }
20
21 int con4(int a, int b)
22 {
23 return a * b - 10;
24 }
25
26 int con5(int a, int b)//直接新增加一个规则
27 {
28 return a + b - 3 + 100 * 2;
29 }
30
31 void test01()
32 {
33 int(*pFunc)(int, int) = con1;//con1可以直接改为con2、con3、con4
34 int ret = pFunc(10, 20);
35 printf("ret = %d\n", ret);
36 }
37
38 //1.函数可以做另外一个函数的参数(定制)
39 void doLogic(int(*pFunc)(int, int))
40 {
41 int a = 10;
42 int b = 20;
43 int ret = pFunc(a, b);
44 printf("ret = %d\n", ret);
45 }
46 void test02()
47 {
48 doLogic(con1);
49 }
50
51 //2.函数指针数组
52 void fun1()
53 {
54 printf("fun1\n");
55 }
56 void fun2()
57 {
58 printf("fun2\n");
59 }
60 void fun3()
61 {
62 printf("fun3\n");
63 }
64 void test03()
65 {
66 void(*func_array[3])();
67 func_array[0] = fun1;
68 func_array[1] = fun2;
69 func_array[2] = fun3;
70
71 for(int i = 0; i < 3; ++i)
72 {
73 func_array[i]();
74 }
75
76 }
77
78 //函数指针做函数参数(回调函数)
79 void printAllArray(void* arr, int eleSize, int len, void(*print)(void*))
80 {
81 char* start = (char*)arr;
82 for(int i = 0; i < len; ++i)
83 {
84 //printf("%d\n", start + i * eleSize);//测试地址
85 char* eleAddr = start + i * eleSize;
86 print(eleAddr);
87 }
88 }
89
90 void MyPrint(void* data)
91 {
92 int* p = (int*)data;
93 printf("%d ", *p);
94 }
95
96 struct Person
97 {
98 char name[64];
99 int age;
100 };
101
102 void MyPrintPerson(void* data)
103 {
104 struct Person* person = (struct Person*)data;
105 printf("Name:%s Age:%d\n", person->name, person->age);
106 }
107
108 void test04()
109 {
110 int arr[] = {1, 2, 3, 4, 5};
111 printAllArray(arr, sizeof(int), 5, MyPrint);
112 //printf("---------------");
113 //for(int i = 0; i < 5; ++i)
114 //{
115 // printf("%d\n", &arr[i]);//测试地址
116 //}
117
118 struct Person persons[] = {
119 {"aaa", 10};
120 {"bbb", 20};
121 {"ccc", 30};
122 {"ddd", 40};
123 {"eee", 50};
124 };
125 printAllArray(persons, sizeof(struct Person), 5, MyPrintPerson);
126 }
127
128
129 int main(){
130
131 //test01();
132 //test02();
133 //test03();
134 test04();
135
136 system("pause");
137 return EXIT_SUCCESS;
138 }
二、链表
1、链表基本概念
(1)什么是链表
·链表是一种常用的数据结构,它通过指针将一些列数据结点,连接成一个数据链。相对于数组,链表具有更好的动态性(非顺序存储)。
·数据域用来存储数据,指针域用于建立与下一个结点的联系。
·建立链表时无需预先知道数据总量的,可以随机的分配空间,可以高效的在链表中的任意位置实时插入或删除数据。
·链表的开销,主要是访问顺序性和组织链的空间损失。
数组和链表的区别?
数组:一次性分配一块连续的存储区域。
优点:随机访问元素效率高
缺点:1)需要分配一块连续的存储区域(很大区域,有可能分配失败)
2)删除和插入某个元素效率低
链表:无需一次性分配一块连续的存储区域,只需分配n块节点存储区域,通过指针建立关系。
优点:1)不需要一块连续的存储区域
2)删除和插入某个元素效率高
缺点:随机访问元素效率低
(2)有关结构体的自身引用
问题1:请问结构体可以嵌套本类型的结构体变量吗?
问题2:请问结构体可以嵌套本类型的结构体指针变量吗?
■结构体可以嵌套另外一个结构体的任何类型变量;
■结构体嵌套本结构体普通变量(不可以)。本结构体的类型大小无法确定,类型本质:固定大小内存块别名;
■结构体嵌套本结构体指针变量(可以),指针变量的空间能确定,32位,4字节,64位,8字节;
(3)链表节点
大家思考一下,我们说链表是由一系列的节点组成,那么如何表示一个包含了数据域和指针域的节点呢?
链表的节点类型实际上是结构体变量,此结构体包含数据域和指针域:
·数据域用来存储数据;
·指针域用于建立与下一个结点的联系,当此节点为尾节点时,指针域的值为NULL;
(4)链表的分类
链表分为:静态链表和动态链表
静态链表和动态链表是线性表链式存储结构的两种不同的表示方式:
·所有结点都是在程序中定义的,不是临时开辟的,也不能用完后释放,这种链表称为“静态链表”。
·所谓动态链表,是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系。
1)静态链表
2)动态链表
3)带头和不带头链表
·带头链表:固定一个节点作为头结点(数据域不保存有效数据),起一个标志位的作用,以后不管链表节点如果改变,此头结点固定不变。
·不带头链表:头结点不固定,根据实际需要变换头结点(如在原来头结点前插入新节点,然后,新节点重新作为链表的头结点)。
4)单向链表、双向链表、循环链表
单向链表:
双向链表:
循环链表:
2、链表基本操作
(1)创建链表
使用结构体定义节点类型:
编写函数:link_node* init_linklist()
建立带有头结点的单向链表循环创建结点结点数据域中的数值从键盘输入以-1作为输入结束标志,链表的头结点地址由函数值返回.
(2)遍历链表
编写函数:void foreach_linklist(link_node* head()
顺序输出单向链表各项结点数据域中的内容:
(3)插入节点
编写函数:void insert_ linklist(link_node* head,int val,int data).
在指定值后面插入数据 data,如果值val不存在,则在尾部插入。
(4)删除节点
编写函数:void remove _linklist(link node* head,int val)
删除第一个值为val的结点.
(5)销毁链表
编写函数:void destroy_linklist(link_node* head)
销毁链表,释放所有节点的空间.