在學習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)
銷毀連結清單,釋放所有節點的空間.