大家好啊,我是小象?(?òωó?)?
我的博客:Xiao Xiangζ?????
很高興見到大家,希望能夠和大家一起交流學習,共同進步。
今天我們一起來學習轉移表,回調函數,qsort…
目錄
- 一、轉移表
- 1.1 定義與原理
- 1.3 優點
- 二、回調函數是什么?
- 1.1 原理
- 1.2 使用方法
- 1.3 優點
- 三、qsort 使用舉例
- 3.1 使用qsort函數排序整型數據
- 3.2 使用qsort排序結構數據
- 四、結尾
一、轉移表
在 C 語言中,轉移表(Jump Table)是一種用于實現多路分支選擇的技術,也被稱為跳轉表或分支表。
1.1 定義與原理
轉移表本質上是一個函數指針數組,數組中的每個元素都是一個指向函數的指針。它的原理是通過計算索引值來選擇調用數組中的某個函數指針,從而實現根據不同條件跳轉到不同代碼段的功能,類似于根據索引查找表中的內容并執行相應操作。
1.3 優點
代碼清晰簡潔:
相比于使用大量的if-else語句或switch語句嵌套,轉移表能使代碼結構更加清晰,邏輯更加直觀。尤其是在處理多個分支情況時,代碼的可讀性更高。
高效性:
在執行多路分支選擇時,轉移表的查找和跳轉操作通常比復雜的if-else或switch判斷更快。因為它主要通過數組索引直接定位到目標函數,而不需要逐個進行條件判斷。
可擴展性:
當需要添加新的分支或功能時,只需在轉移表中添加相應的函數指針,并實現對應的函數邏輯即可,對原有代碼的改動較小,易于維護和擴展。
舉例:計算器的?般實現:
#include <stdio.h>
int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int mul(int a, int b)
{return a * b;
}
int div(int a, int b)
{return a / b;
}
int main()
{int x, y;int input = 1;int ret = 0;do{printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf(" 0:exit \n");printf("*************************\n");printf("請選擇:");scanf("%d", &input);switch (input){case 1:printf("輸?操作數:");scanf("%d %d", &x, &y);ret = add(x, y);printf("ret = %d\n", ret);break;case 2:printf("輸?操作數:");scanf("%d %d", &x, &y);ret = sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("輸?操作數:");scanf("%d %d", &x, &y);ret = mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("輸?操作數:");scanf("%d %d", &x, &y);ret = div(x, y);printf("ret = %d\n", ret);break;case 0:printf("退出程序\n");break;default:printf("選擇錯誤\n");break;}} while (input);return 0;
}
使?函數指針數組的實現:
#include <stdio.h>
int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int mul(int a, int b)
{return a * b;
}
int div(int a, int b)
{return a / b;
}
int main()
{int x, y;int input = 1;int ret = 0;int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //轉移表do{printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf(" 0:exit \n");printf("*************************\n");printf("請選擇:");scanf("%d", &input);if ((input <= 4 && input >= 1)){printf("輸?操作數:");scanf("%d %d", &x, &y);ret = (*p[input])(x, y);printf("ret = %d\n", ret);}else if (input == 0){printf("退出計算器\n");}else{printf("輸?有誤\n");}} while (input);return 0;
}
二、回調函數是什么?
回調函數是一個作為參數傳遞給另一個函數的函數,而接收回調函數作為參數的函數會在合適的時候調用這個傳遞進來的函數。簡單來說,就是 A 函數將 B 函數作為參數傳遞給 C 函數,C 函數在某個時刻調用 B 函數,此時 B 函數就是回調函數。
1.1 原理
回調函數的核心原理基于函數指針。在 C 語言中,函數名本質上代表該函數的入口地址,而函數指針則可以存儲這個地址。通過將函數指針作為參數傳遞給另一個函數,接收該參數的函數就可以在需要的時候通過這個指針調用對應的函數。
1.2 使用方法
以下是使用回調函數的基本步驟:
定義回調函數:首先要定義一個符合特定參數和返回值要求的函數,這個函數將作為回調函數使用。
定義接收回調函數作為參數的函數:該函數需要有一個函數指針類型的參數,用于接收回調函數的地址。
調用接收回調函數的函數:在調用時,將回調函數的名稱作為參數傳遞給接收回調函數的函數。
1.3 優點
靈活性:通過回調函數,可以在不修改接收回調函數的函數的源代碼的情況下,改變其行為。只需要傳遞不同的回調函數,就可以實現不同的功能。
代碼復用:可以將一些通用的邏輯封裝在接收回調函數的函數中,而將具體的處理邏輯放在回調函數中。這樣,不同的回調函數可以復用接收回調函數的函數的代碼。
異步編程:在異步編程中,回調函數非常有用。當某個操作完成時,可以通過回調函數通知調用者進行后續處理。
//使?回調函數改造前
#include <stdio.h>
int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int mul(int a, int b)
{return a * b;
}
int div(int a, int b)
{return a / b;
}
int main()
{int x, y;int input = 1;int ret = 0;do{printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf("*************************\n");printf("請選擇:");scanf("%d", &input);switch (input){case 1:printf("輸?操作數:");scanf("%d %d", &x,&y);ret = add(x, y);printf("ret = %d\n",ret);break;case 2:printf("輸?操作數:");scanf("%d %d", &x,&y);ret = sub(x, y);printf("ret = %d\n",ret);break;case 3:printf("輸?操作數:");scanf("%d %d", &x,&y);ret = mul(x, y);printf("ret = %d\n",ret);break;case 4:printf("輸?操作數:");scanf("%d %d", &x,&y);ret = div(x, y);printf("ret = %d\n",ret);break;case 0:printf("退出程序\n");break;default:printf("選擇錯誤\n");break;}} while (input);return 0;
三、qsort 使用舉例
qsort 是 C 標準庫中用于對數組進行快速排序的函數,它基于快速排序算法實現,能對任意類型的數組進行排序。
3.1 使用qsort函數排序整型數據
#include <stdio.h>
//qosrt函數的使?者得實現?個?較函數
int int_cmp(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}
int main()
{int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int i = 0;qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}printf("\n");return 0;
}
3.2 使用qsort排序結構數據
struct Stu //學?
{char name[20];//名字int age;//年齡
};
//假設按照年齡來?較
int cmp_stu_by_age(const void* e1, const void* e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//strcmp - 是庫函數,是專??來?較兩個字符串的??的
//假設按照名字來?較
int cmp_stu_by_name(const void* e1, const void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//按照年齡來排序
void test2()
{struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
//按照名字來排序
void test3()
{struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{test2();test3();return 0;
}
注意事項
比較函數的實現:比較函數的實現要正確,確保能準確反映元素之間的大小關系。不同的比較函數實現可以實現升序、降序或其他自定義的排序規則。
類型轉換:在比較函數中,由于 qsort 函數的通用性,傳入的參數是 const void *
類型,需要將其轉換為實際的數據類型才能進行比較操作。 穩定性:qsort
是不穩定的排序算法,即相等元素的相對順序在排序后可能會改變。如果需要穩定的排序算法,可以考慮使用其他排序函數或自行實現穩定排序。
性能:qsort 平均時間復雜度為 (O(n log n)),但在最壞情況下時間復雜度為
(O(n^2))。不過在大多數實際應用場景中,它的性能表現良好。
四、結尾
這一課的內容就到這里了,下節課繼續學習指針的其他一些知識
如果內容有什么問題的話歡迎指正,有什么問題也可以問我!