一. 回調函數
? 1. 什么是回調函數?
回調函數(Callback Function)是通過?函數指針?調用的函數。其本質是:
將函數作為參數傳遞給另一個函數,并在特定條件下被調用,實現?反向控制。
? 2. 回調函數的使用
回調函數的主要用途包括事件處理和排序算法。通過傳遞函數指針作為參數,可以實現代碼的靈活性和可重用性。例如,在一個簡單的計算器程序中,可以通過傳遞不同的計算函數(如加法、減法等)來簡化代碼。
? 3. 示例代碼
以下是一個簡單的回調函數示例,展示了如何使用回調函數進行加法和減法操作:
#include <stdio.h>// 定義回調函數類型
typedef int (*Operation)(int, int);// 定義加法函數
int add(int a, int b)
{return a + b;
}// 定義減法函數
int jian(int a, int b)
{return a - b;
}// 使用回調函數進行計算
int calculate(int a, int b, Operation op){return op(a, b);
}int main()
{int x = 10, y = 5;printf(" %d\n", calculate(x, y, add));printf(" %d\n", calculate(x, y, jian));return 0;
}
二.? ? qsort函數
1 qsort 函數的定義
qsort
?函數是C語言標準庫中的一個排序函數,用于對數組進行快速排序。它的完整聲明如下:
#include <stdlib.h>
void qsort(void *base, size_t nmemb, size_t size,int (*compar)(const void * e1, const void * e2));
base
:指向要排序的數組的第一個元素的指針。nmemb
:數組中元素的個數。size
:數組中每個元素的大小,以字節為單位。compar
:用來比較兩個元素的函數,即函數指針(回調函數)。
2.compar參數
- compar參數指向一個比較兩個元素的函數。比較函數的原型應該像下面這樣。注意兩個形參必須是const void *型,同時在調用compar 函數(compar實質為函數指針,這里稱它所指向的函數也為compar)時,傳入的實參也必須轉換成const void *型。在compar函數內部會將const void *型轉換成實際類型。
- int compar(const void *e1, const void *e2);
- 如果compar返回值小于0(< 0),那么e1所指向元素會被排在e2所指向元素的左面;
- 如果compar返回值等于0(= 0),那么e1所指向元素與e2所指向元素的順序不確定;
- 如果compar返回值大于0(> 0),那么e1所指向元素會被排在e2所指向元素的右面。
這里再強調一下?
? ?參數類型:必須為? ?const void*
參考代碼:?
以下是一個使用?qsort
?函數對整型數組進行升序排序的示例:
qsort? 一般默認是升序
#include <stdio.h>
#include <stdlib.h>int comp(const void* e1, const void* e2)
{return (*(int*)e1 - *(int*)e2);
}
int main()
{int arr[6] = { 44,0,520,1314,888,444};int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), comp);/*for (int i = 0; i < sz; i++)printf("%d ", arr[i]);*/return 0;
}
運行結果:?
以下是一個使用?qsort
?函數對結構體數組進行排序的示例,?
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct student
{int id;char name[10];int grade;
}student;int cmp1(const void *a, const void *b)//一級排序
{student *s1 = (student*)a;student *s2 = (student*)b;return s1->id - s2->id;
}int cmp2(const void *a,const void *b)//二級排序
{student *s1 = (student*)a;student *s2 = (student*)b;if(strcmp(s1->name , s2->name) != 0)return strcmp(s1->name , s2->name); else return s1->id - s2->id;
}int cmp3(const void *a,const void *b)//三級排序
{student *s1 = (student*)a;student *s2 = (student*)b;if(s1->grade != s2->grade) return s1->grade - s2->grade;else{if(strcmp(s1->name , s2->name) != 0)return strcmp(s1->name , s2->name);elsereturn s1->id - s1->id;}
}int main()
{int i,N,C;scanf("%d %d",&N,&C);student *stu;stu=(student*)malloc(N*sizeof(student));for(i = 0 ; i < N ; i++)scanf("%d %s %d" , &stu[i].id , stu[i].name , &stu[i].grade);switch(C){case 1: qsort(stu, N, sizeof(student), cmp1);break;//一級排序case 2: qsort(stu, N, sizeof(student), cmp2);break;//二級排序case 3: qsort(stu, N, sizeof(student), cmp3);break;//三級排序}printf("排序結果:\n");for(i = 0 ; i < N ; i++)printf("%03d %s %d\n" , stu[i].id , stu[i].name , stu[i].grade);return 0;
}
三 .調試技巧與常見陷阱
1. 調試建議
-
在比較函數中添加打印語句:
-
int compare_debug(const void *a, const void *b){ int x = *(int*)a; int y = *(int*)b;printf("Comparing %d vs %d\n", x, y); return x - y; }
2. 常見錯誤
掌握回調函數與qsort的配合使用,不僅能寫出更通用的代碼,更能深入理解C語言"函數即數據"的哲學思想。這種設計模式在文件操作、事件處理、算法策略等場景中廣泛應用,是提升編程能力的重要階梯。
-
錯誤1:忘記類型轉換
// 錯誤寫法: return *a - *b; // a和b是void指針!// 正確: return *(int*)a - *(int*)b;
-
錯誤2:整數溢出
-
?int compare_unsafe(const void *a, const void *b) {return *(int*)a - *(void*)b;// 可能溢出! }// 改進版:int compare_safe(const void *a, const void *b) { int x = *(int*)a; int y = *(int*)b; return (x > y) ? 1 : ((x < y) ? -1 : 0); }
-
錯誤3:修改const數據
int compare_wrong(const void *a, const void *b) {*(int*)a = 10;// 嘗試修改原始數據!return ...;}
四.最佳實踐總結
-
嚴格遵循比較函數規范
確保返回正確的-1/0/1,而不僅僅是差值 -
優先使用const修飾
比較函數參數應聲明為const void*
-
復雜結構預先處理
對頻繁排序的大型結構,可建立索引數組 -
跨平臺注意字節序
處理網絡傳輸數據時考慮大小端問題 -
性能關鍵處慎用
高頻調用場景可考慮特定優化