?
目錄
前言?
一. 回調函數是什么?
?1.定義
2.?代碼示例:計數器
2.1 使用回調函數改造前
? 2.2 使用回調函數改造后
二. qsort使用舉例
1.? qsort介紹
2.?使用qsort函數排序整型數據
3.?使用qsort排序結構體數據
三. qsort函數的模擬實現
四. sizeof和strlen的對比
1.?sizeof
2.?strlen
3.?sizeof 和 strlen的對比
寫在最后
前言?
C語言指針詳解(一)https://blog.csdn.net/qq_51904510/article/details/136810287
C語言指針詳解(二)https://blog.csdn.net/qq_51904510/article/details/138172497
前面我們了解了指針的指針的指針的絕大多數知識,現在,我們來了解回調函數的定義、使用以及意義,了解可以快速排序元素的函數——qsort的使用方法和模擬實現,以及sizeof和strlen的對比
一. 回調函數是什么?
?1.定義
回調函數就是一個通過函數指針調用的函數。
如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,被調用的函數就是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。
之前我們寫的計數器的實現的代碼中,有一部分的代碼是重復出現的,其中雖然執行計算的邏輯是區別的,但是輸入輸出操作是冗余的,有沒有辦法,簡化一些呢?
因為紅色框中的代碼,只有調用函數的邏輯是有差異的,我們可以把調用的函數的地址以參數的形式傳遞過去,使用函數指針接收,函數指針指向什么函數就調用什么函數,這里其實使用的就是回調函數的功能。
2.?代碼示例:計數器
2.1 使用回調函數改造前
#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;
}
? 2.2 使用回調函數改造后
#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;
}void calc(int(*pf)(int, int))
{int ret = 0;int x, y;printf("輸入操作數:");scanf("%d %d", &x, &y);ret = pf(x, y);printf("ret = %d\n", ret);
}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:calc(add);break;case 2:calc(sub);break;case 3:calc(mul);break;case 4:calc(div);break;case 0:printf("退出程序\n");break;default:printf("選擇錯誤\n");break;}} while (input);return 0;
}
?我們可以發現,在使用回調函數后,有效的減少了代碼量,通過傳遞函數指針的形式,完成了計數器不同計算功能的實現,同時減少了大量的重復代碼部分
二. qsort使用舉例
1.? qsort介紹
qsort可以對數組的元素進行排序
對數組的元素進行排序,每個元素的字節長度為 size,使用自己編寫的compar函數確定順序。
此函數使用的排序算法通過調用指定的函數來比較元素對,并將指向它們的指針作為參數。
該函數不返回任何值,而是通過重新排序其元素來修改指向的數組的內容
使用時需要包含庫函數<stdlib.h>
函數原型為:
void qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*));
?參數介紹:
返回值:
該函數無返回值
相當于:
void qsort (數組指針, 數組元素個數, 每個元素大小,自己編寫的比較大小的函數指針);
注意:qsort與bubble_sort時間復雜度相同,均為O()?
2.?使用qsort函數排序整型數據
#include <stdio.h>
#include <stdlib.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.?使用qsort排序結構體數據
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Stu //學?
{char name[20];//名字int age;//年齡
};
//打印結構體
void printstruct(struct Stu* s, size_t sz)
{for (int i = 0; i < sz; i++){printf("%s %d ", s[i].name, s[i].age);if (i == sz - 1){printf("\n");}}
}
//假設按照年齡來比較
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);printstruct(&s, sz);
}
//按照名字來排序
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);printstruct(&s, sz);
}
int main()
{test2();test3();return 0;
}
三. qsort函數的模擬實現
使用回調函數,模擬實現qsort(采用冒泡的方式)。
注意:這里第一次使用 void* 的指針。
#include <stdio.h>
int int_cmp(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}
void _swap(void* p1, void* p2, int size)
{int i = 0;for (i = 0; i < size; i++){char tmp = *((char*)p1 + i);*((char*)p1 + i) = *((char*)p2 + i);*((char*)p2 + i) = tmp;}
}
void bubble(void* base, int count, int size, int(*cmp)(void*, void*))
{int i = 0;int j = 0;for (i = 0; i < count - 1; i++){for (j = 0; j < count - i - 1; j++){if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0){_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}
}
int main()
{int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int i = 0;bubble(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;
}
四. sizeof和strlen的對比
1.?sizeof
在學習操作符的時候,我們學習了 sizeof , sizeof 計算變量所占內存內存空間大小的,單位是字節,如果操作數是類型的話,計算的是使用類型創建的變量所占內存空間的大小。
sizeof 只關注占用內存空間的大小,不在乎內存中存放什么數據。
比如:
#include <stdio.h>
int main()
{int a = 10;printf("sizeof(a) = %d\n", sizeof(a));printf("sizeof a = %d\n", sizeof a);printf("sizeof(int) = %d\n", sizeof(int));return 0;
}
2.?strlen
strlen 是C語言庫函數,功能是求字符串長度。函數原型如下:
size_t strlen ( const char * str );
統計的是從 strlen 函數的參數 str 中這個地址開始向后, \0 之前字符串中字符的個數。
strlen 函數會一直向后找 \0 字符,直到找到為止,所以可能存在越界查找。
#include <stdio.h>
#include <string.h>
int main()
{char arr1[3] = { 'a', 'b', 'c' };char arr2[] = "abc";printf("strlen(arr1) = %d\n", strlen(arr1));printf("strlen(arr2) = %d\n", strlen(arr2));printf("sizeof(arr1) = %d\n", sizeof(arr1));printf("sizeof(arr2) = %d\n", sizeof(arr2));return 0;
}
3.?sizeof 和 strlen的對比
sizeof | strlen |
---|---|
sizeof是操作符 | strlen是庫函數,使用需要包含頭文件 string.h |
sizeof計算操作數所占內存的大小,單位是字節 | srtlen是求字符串長度的,統計的是 \0 之前字符的隔個數 |
sizeof不關注內存中存放什么數據 | 關注內存中是否有 \0 ,如果沒有 \0 ,就會持續往后找,可能會越界 |
寫在最后
到這里我們就了解了指針的絕大多數知識,仔細想想,也不像網上說得那樣難以理解吧,我們可能會認為C語言對指針的管理太松,很多問題都需要程序員自己去發現,去解決。然而,正是因為C語言對程序員十分放心,沒有引入太多的檢查,所以C語言的運行速度相當快,許多操作系統的內核如Windows也是用C語言編寫,C語言的許多庫函數運行效率也是相當高的。C語言從誕生到現在,經歷了時間的考驗,說明其本身是一個十分優秀的語言,值得我們去用心學習。