目錄
1.字符指針
2.指針數組
3.數組指針?
3.1 數組指針的定義
3.2 數組指針的使用?
4.數組參數,指針參數
4.1 一維數組傳參?
4.2 二維數組傳參?
4.3 一級指針傳參?
4.4 二級指針傳參
5.函數指針
?6.函數指針數組
6.1函數指針數組的定義
6.2 函數指針數組的使用
7.指向函數指針數組的指針
8.回調函數
?8.1 回調函數的用例——qsort
在指針初階中,我們知道了指針的概念:內存單元有編號,編號=地址=指針1. 指針(口頭語)就是個變量,用來存放地址,地址唯一標識一塊內存空間。2. 指針的大小是固定的4/8個字節(32位平臺/64位平臺)。3. 指針是有類型,指針的類型決定了指針的+-整數的步長,指針解引用操作的時候的權限。4. 指針的運算。
1.字符指針
字符指針的常見用法:
int main() {char ch = 'w';char* pc = &ch;*pc = 'w';return 0;
}
可以使用常量字符串給字符指針賦值:常量字符串表達式的值就是首字符的地址,在內存中連續存放且可以通過下標訪問,可以看成是數組,但是不能改變
#include <stdio.h>
int main() {char* p = "abcdef";//加const修飾指針更安全printf("%s\n", p);printf("%c\n", *p);//*p得到的是'a',需要用%c的格式打印*p = 'e';//err:常量字符串不能改變printf("%c\n", "abcdef"[3]);//通過下標訪問return 0;
}
常量字符串給字符指針賦值時,使用const修飾指針,如果解引用改變字符值會報編譯錯誤,更安全合理?
?練習題:
#include <stdio.h>
int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";const char* str3 = "hello bit.";const char* str4 = "hello bit.";if (str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}
輸出:
str1 and str2 are not same
用相同的常量字符串去初始化不同的數組的時候就會開辟出不同的內存塊。數組名表示首元素地址,str1,str2分別指向兩塊內存區域
str3 and str4 are same注意:常量區就是一直存在的,只讀的,不可更改的數據區域,并且一個字符串只會有一份
str3,str4指向同一個常量字符串,但str3,str4是不同的內存區域
2.指針數組
指針數組是一個存放指針的數組: int* arr[10] = {0};//表示arr數組有10個元素,且每一個元素都為int*類型
int main() {int a = 0;int b = 0;int c = 0;int d = 0;//指針數組不會這樣使用int* arr[] = { &a,&b,&c,&d };return 0;
}
指針數組的用法:
- 模擬一個二維數組
- 管理多個字符串
#include <stdio.h>
//指針數組模擬二維數組
int main() {int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };//數組名表示首元素地址int* arr[] = { arr1,arr2,arr3 };//訪問int i = 0;for (i = 0; i < 3; i++) {int j = 0;for (j = 0; j < 5; j++) {printf("%d ", arr[i][j]);(*(arr+i)//?}printf("\n");}return 0;
}
#include <stdio.h>
//管理多個字符串
int main() {char* arr[4] = { "wo","shi","hao","dan" };//訪問int i = 0;for (i = 0; i < 4; i++) {printf("%s\n", arr[i]);
//arr[i]->*(arr+i),arr是指針數組,數組名表示首元素地址,解引用得到char*訪問字符串,以%s打印,不同于*p(p為字符指針)只能以%c格式打印}return 0;
}
3.數組指針?
3.1 數組指針的定義
能夠指向數組的指針。?
數組名是首元素的地址
但存在兩個例外:
1.sizeof(數組名),這里的數組表示整個數組,sizeof(數組名)計算的是整個數組的大小,單位是字節
2.&數組名,這里的數組名表示整個數組,取出的是數組的地址
數組的地址怎么理解,看下面的代碼:
#include <stdio.h>
int main()
{int arr[10] = { 0 };printf("arr = %p\n", arr);//int*printf("arr+1 = %p\n", arr + 1);printf("&arr[0] = %p\n", &arr[0]);//int*printf("&arr[0]+1 = %p\n", &arr[0] + 1);printf("&arr= %p\n", &arr);//p存放數組的地址,是數組指針printf("&arr+1= %p\n", &arr + 1);return 0;
}
運行結果:
arr = 00000056FEAFFC68
arr+1 = 00000056FEAFFC6C//加4個字節
&arr[0] = 00000056FEAFFC68
&arr[0]+1 = 00000056FEAFFC6C//加4個字節
&arr= 00000056FEAFFC68
&arr+1= 00000056FEAFFC90//加40個字節
指針類型決定了指針+1的步長是幾個字節
數組指針的寫法由語法規定
#include <stdio.h>
int main() {//整型指針int a = 0;int* p = &a;//數組指針int arr[] = { 0 };int (*p)[1] = &arr;return 0;
}
注意:數組指針的大小不能省略,數組即使初始化沒有指定大小,大小也是固定的
p的類型是int (*)[1],大小不寫默認為0
3.2 數組指針的使用?
訪問一維數組使用數組指針?
#include <stdio.h>
int main() {int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int (*p)[10] = &arr;int i = 0;for (i = 0; i < 10; i++) {printf("%d ", (*p)[i]);}return 0;
}
(*p)->*&arr->*&可以抵消,使用數組指針訪問數組過于繁瑣,不建議
一般使用整型指針訪問數組:?
#include <stdio.h>
int main() {int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = arr;int i = 0;for (i = 0; i < 10; i++) {printf("%d ", p[i]);//*(p+i)}return 0;
}
?數組傳參:
一維數組:
#include <stdio.h>
//一維數組傳參——形參為數組形式
void Print(int arr[], int sz) {int i = 0;for (i = 0; i < sz; i++) {printf("%d ", arr[i]);}
}
int main() {int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);Print(arr, sz);return 0;
}
形參數組的大小可以不寫或者寫錯,因為數組傳參的本質是傳數組首元素的地址?
#include <stdio.h>
//一維數組傳參——形參為指針形式
void Print(int* arr, int sz) {int i = 0;for (i = 0; i < sz; i++) {printf("%d ", arr[i]);}
}
int main() {int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);Print(arr, sz);return 0;
}
二維數組:
注意:二維數組是一維數組的數組,二維數組的數組名為首元素地址,二維數組的首元素是第一行的地址;二維數組傳參,形參為數組時,形參的行可以省略,列不行
#include <stdio.h>
//二維數組傳參——參數為數組形式
void Print(int arr[3][5], int r, int c) {int i = 0;for (i = 0; i < r; i++) {int j = 0;for (j = 0; j < c; j++) {printf("%d ", arr[i][j]);}printf("\n");}
}
int main() {int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6}, {3,4,5,6,7} };Print(arr, 3, 5);return 0;
}
#include <stdio.h>
//二維數組傳參——參數為指針形式
void Print(int (*p)[5], int r, int c) {int i = 0;for (i = 0; i < r; i++) {int j = 0;for (j = 0; j < c; j++) {printf("%d ", p[i][j]);}printf("\n");}
}
int main() {int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6}, {3,4,5,6,7} };Print(arr, 3, 5);return 0;
}
注意:p為二維數組指針,表示二維數組的第一行地址,加i表示第i行的地址,解引用得到第i行的數組名,*p=arr,*(p+i)=arr[i];第i行的數組名既不與&結合,也沒有單獨放在sizeof中,所以表示第i行首元素的地址,再使用j訪問第i行的每個元素,p[i][j]表示*(*(p+i)+j)
?分析:
4.數組參數,指針參數
4.1 一維數組傳參?
#include <stdio.h>
//一維數組傳參
void test(int arr[])//ok?
{
}
void test(int arr[10])//ok?
{
}
void test(int* arr)//ok?
{
}
void test2(int* arr[20])//ok?
{
}
void test2(int** arr)//ok?//使用二級指針存放一級指針
{
}
int main()
{int arr[10] = { 0 };int* arr2[20] = { 0 };test(arr);test2(arr2);
}
全部正確
4.2 二維數組傳參?
#include <stdio.h>
//二維數組傳參
void test(int arr[3][5])//ok?
{
}
void test(int arr[][])//ok?//err
{
}
void test(int arr[][5])//ok?
{
}
//總結:二維數組傳參,函數形參的設計只能省略第一個[]的數字。
//因為對一個二維數組,可以不知道有多少行,但是必須知道一行多少元素。
//這樣才方便運算。
void test(int* arr)//ok?//err
{
}
void test(int* arr[5])//ok?//err
{
}
void test(int (*arr)[5])//ok?
{
}
void test(int** arr)//ok?//err
{
}
int main()
{int arr[3][5] = { 0 };test(arr);
}
二維數組傳參,形參為數組時列不能省略,形參為指針時,只能用數組指針,來接收二維數組第一行的地址
4.3 一級指針傳參?
#include <stdio.h>
void print(int* p, int sz) {int i = 0;for (i = 0; i < sz; i++){printf("%d\n", *(p + i));}
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9 };int* p = arr;int sz = sizeof(arr) / sizeof(arr[0]);//一級指針p,傳給函數print(p, sz);return 0;
}
一級指針傳參,形參用一級指針接收就行
注意:反向思考,一級指針的形參,可以接收什么實參
void test(int *p) {}
int main() {int a = 10;int* ptr = &a;int arr[5];test(&a);//傳整型變量的地址test(ptr);//傳整型指針test(arr);//傳整型一維數組的數組名return 0;
}
4.4 二級指針傳參
#include <stdio.h>
void test(int** ptr) {
printf("num = %d\n", **ptr);
}
int main()
{int n = 10;int* p = &n;int** pp = &p;test(pp);test(&p);return 0;
}
反向思考:
void test(int** p) {}
int main() {int a = 10;int* p = &a;int** pp = &p;int* arr[6];test(&p);//傳一級指針的地址test(pp); // 傳二級指針變量test(arr);//傳指針數組的數組名return 0;
}
5.函數指針
數組指針——指向數組的指針——存放數組的地址——&數組名就是數組的地址
函數指針——指向函數的指針——存放函數的地址——如何得到函數的地址?
#include <stdio.h>
int Add(int x, int y) {return x + y;
}
int main() {printf("%p\n", &Add);printf("%p\n", Add);//函數指針int (*pf1)(int, int) = Add;int (*pf2)(int, int) = &Add;//通過函數指針解引用調用函數int ret = (*pf2)(2, 3);printf("%d\n", ret);//直接通過函數指針調用//函數直接通過函數名調用,函數名就是函數指針,所以指針可以不解引用來調用函數,*是擺設,可以沒有,可以有多個//注意:要么不解引用,解引用一定要加括號ret = pf1(4, 6);printf("%d\n", ret);return 0;
}
通過運行調試以及將Add和&Add都賦值給相同的類型int (*)(int,int)沒有警告可以看出,Add,&Add類型相同,都表示函數地址
函數指針的寫法:?
int? ? ? ? ? ? ? ? (*)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??(int,int)
返回類型? ? ?表示類型為指針? ? 參數類型(參數名一般省略,只有函數定義時才用到參數名)
分析:
int main() {//分析//代碼1 (*(void (*)())0)();//void (*)()是函數指針類型,在括號中表示強制類型轉換,對0進行從整型強轉為指針類型,存放一個函數地址,表示0地址處放一個函數,返回類型為void,沒有參數//表示解引用調用0地址處函數,函數沒有參數,沒有傳參//代碼2void (*signal(int, void(*)(int)))(int);//是一個函數聲明,聲明函數signal,參數類型為int,和void(*)(int),該函數指針參數為int,返回類型為void,signal函數的返回類型為函數指針void (*)(int),該函數指針參數為int,返回類型為void//簡化//void(*)(int) signal(int, void(*)(int));//error:錯誤寫法,語法不支持;函數指針作為返回值,名字要寫在*旁邊//typedef void (*)(int) pfun_t;//簡化:重命名函數指針類型,但是pfun_t要放到*旁邊typedef void (*pfun_t)(int);//去掉typedef,pfun_t是函數指針變量,加上typedef,pfun_t是函數指針類型pfun_t signal(int, pfun_t);return 0;
}
?6.函數指針數組
6.1函數指針數組的定義
int Add(int x, int y) {return x + y;
}
int Sub(int x, int y) {return x - y;
}
int main() {int (*pf1)(int, int) = Add;int (*pf2)(int, int) = Sub;//數組中存放多個類型相同的元素//函數指針數組int (*pfArr[2])(int, int) = { Add,Sub };//pfArr是函數指針數組,是存放函數指針的數組//數組名:pfArr;數組元素個數:2;數組元素類型:int (*)(int, int)return 0;
}
6.2 函數指針數組的使用
完成一個計算器
switch語句實現:
#include <stdio.h>
void menu() {printf("***************************\n");printf("**** 1.add 2.sub ****\n");printf("**** 3.mul 4.div ****\n");printf("**** 0.exit ****\n");printf("***************************\n");
}
int Add(int x, int y) {return x + y;
}
int Sub(int x, int y) {return x - y;
}
int Mul(int x, int y) {return x* y;
}
int Div(int x, int y) {return x / y;
}
int main() {int input = 0;int x = 0;int y = 0;int ret = 0;do {menu();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>
void menu() {printf("***************************\n");printf("**** 1.add 2.sub ****\n");printf("**** 3.mul 4.div ****\n");printf("**** 0.exit ****\n");printf("***************************\n");
}
int Add(int x, int y) {return x + y;
}
int Sub(int x, int y) {return x - y;
}
int Mul(int x, int y) {return x * y;
}
int Div(int x, int y) {return x / y;
}
int main() {int input = 0;int x = 0;int y = 0;int ret = 0;do {menu();printf("請選擇:");scanf("%d", &input);int (*pfArr[])(int, int) = { NULL,Add,Sub,Mul,Div };//函數指針數組大小可以省略// 0 1 2 3 4if (input == 0)printf("退出游戲\n");else if (input <= 4 && input >= 1) {printf("請輸入兩個數:\n");scanf("%d %d", &x, &y);ret = pfArr[input](x, y);//通過函數名(函數地址)調用函數printf("ret = %d\n", ret);}elseprintf("選擇錯誤,請重新選擇\n");} while (input);return 0;
}
后續增加運算時,只需要增加pfArr中的元素即可,其他main函數中的語句不變
約束:放入pfArr中的函數必須類型一樣
7.指向函數指針數組的指針
不重要,拓展
#include <stdio.h>
int Add(int x, int y) {return x + y;
}
int Sub(int x, int y) {return x - y;
}
int Mul(int x, int y) {return x * y;
}
int Div(int x, int y) {return x / y;
}
int main() {int a = 0;int b = 0;int c = 0;int arr[] = { &a,&b,&c };int* (*p)[3] = &arr;//指向整型指針數組的指針//函數指針數組int (*pfArr[])(int,int) = {NULL,Add,Sub,Mul,Div};int (*(*p)[5])(int, int) = &pfArr;//指向函數指針數組的指針//p指針,指向的類型為int (* [5])(int, int),是一個函數指針數組return 0;
}
8.回調函數
回調函數就是一個通過函數指針調用的函數。如果把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。
使用switch語句實現一個計算器程序時太過冗余,把相同代碼封裝:
#include <stdio.h>
void menu() {printf("***************************\n");printf("**** 1.add 2.sub ****\n");printf("**** 3.mul 4.div ****\n");printf("**** 0.exit ****\n");printf("***************************\n");
}
int Add(int x, int y) {return x + y;
}
int Sub(int x, int y) {return x - y;
}
int Mul(int x, int y) {return x * y;
}
int Div(int x, int y) {return x / y;
}
void calc(int (*pf)(int, int)) {int x = 0;int y = 0;int ret = 0;printf("請輸入兩個數:\n");scanf("%d %d", &x, &y);ret = pf(x, y);printf("ret = %d\n", ret);
}
int main() {int input = 0;do {menu();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;
}
當通過函數指針pf調用Add,Add就稱為回調函數,通過回調函數,可以將函數calc變得更加通用
?8.1 回調函數的用例——qsort
qsort函數是一個庫函數,底層使用的是快速排序的方式,對任意類型數據進行排序
這個函數可以直接使用
數據排序:冒泡排序、選擇排序、插入排序、快速排序...(數據結構)
qsort函數:利用了函數指針實現回調函數的機制
?p1指向的元素>p2指向的元素,返回>0的數字
?p1指向的元素<p2指向的元素,返回<0的數字
?p1指向的元素==p2指向的元素,返回0
qsort函數的使用:
比較整型數組:?
#include <stdio.h>
#include <stdlib.h>
void Print(int arr[], int sz) {int i = 0;for (i = 0; i < sz; i++) {printf("%d ", arr[i]);}printf("\n");
}
//由程序員編寫
int cmp_int(const void* e1, const void* e2) {return *(int*)e1 - *(int*)e2;
//強轉為int*類型才能解引用
}
void test1() {int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);Print(arr, sz);qsort(arr, sz,sizeof(arr[0]),cmp_int);Print(arr, sz);
}
int main() {test1();return 0;
}
?注意:
- 需要包含頭文件stdlib.h
- void* 類型的指針-不能進行解引用,也不能進行+-整數的操作,void*是無具體類型的指針,用來存放任意類型的數據的地址,以保證qsort函數可以排序任意類型數據
比較結構體數據:
- 按照年齡
- 按照名字(字典序)?
按照年齡:
#include <stdio.h>
#include <stdlib.h>
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;
}void test2() {struct Stu arr[] = { {"zhangsan",20},{"lisi",18},{"wangwu",30} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}
int main() {test2();return 0;
}
按照名字:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Stu {char name[20];int age;
};
int cmp_stu_by_name(const void* e1, const void* e2) {return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void test3() {struct Stu arr[] = { {"zhangsan",20},{"lisi",18},{"wangwu",30} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
int main() {test3();return 0;
}
排序后:?
快速排序與冒泡排序相比,將比較方法抽離出來,使其能比較任意類型
冒泡排序:
#include <stdio.h>
//冒泡排序算法——給一組整型數據,使用冒泡排序,兩兩相鄰元素比較,對數據進行升序
//n個元素,比較n-1趟,每趟排序比較n-1-i對
void Print(int arr[], int sz) {int i = 0;for(i = 0; i < sz; i++) {printf("%d ", arr[i]);}
}
void bubble_sort(int arr[], int sz) {//趟數int i = 0;for (int i = 0; i < sz - 1; i++) {int j = 0;//對數for (j = 0; j < sz - 1 - i; j++) {if (arr[j] > arr[j + 1]) {int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}
int main() {int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz);Print(arr, sz);return 0;
}
函數缺陷:只能排序整型數組?
使用冒泡排序模擬一個排序,可以排序任意類型數據:
使用冒泡排序模擬,對不同類型數據的排序來說,排序的趟數和每趟要比較的對數是不變的,只需要改變比較的方法
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//使用冒泡排序模擬任意類型的排序
void Print(int arr[], int sz) {int i = 0;for (i = 0; i < sz; i++) {printf("%d ", arr[i]);}printf("\n");
}
struct Stu {
char name[20];
int age;
};
//固定函數
void swap(char* buf1, char* buf2, size_t size) {int i = 0;for (i = 0; i < size; i++) {char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
//固定函數
//e1,e2是指針,存放一個要比較的元素
//e1指向的元素 > e2指向的元素,返回 > 0的數字
//e1指向的元素 < e2指向的元素,返回 < 0的數字
//e1指向的元素 == e2指向的元素,返回0
void bubble_sort(void* base, size_t sz,size_t size,int (*cmp)(const void* e1,const void* e2)) {//趟數int i = 0;for (int i = 0; i < sz - 1; i++) {int j = 0;//對數for (j = 0; j < sz - 1 - i; j++) {/*if (arr[j] > arr[j + 1]) {int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}*///不能直接用>比較//arr[j]和arr[j+1]的地址如何表示?//base是void*的指針,指向數組首元素,不能+-數字,可以轉成char*類型后加跳過的字節數得到需要的地址 //char* + 1 = 1 * sizeof(char)//int* + 1 = 1 * sizeof(int)if (cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0) {//交換:參數為兩個元素的地址以及元素的大小,將每個元素的對應字節一一交換swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}
}
//使用該排序函數的人提供一個比較方法
int cmp_stu_by_name(const void* e1, const void* e2) {return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);//注意:strcmp對應字節逐一比較,不能比較漢字,一個漢字占兩個字節
}
int cmp_stu_by_age(const void* e1, const void* e2) {return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int cmp_int(const void* e1, const void* e2) {return *(int*)e1 - *(int*)e2;
}
//需要排序的人編寫
void test3() {struct Stu arr[] = { {"zhangsan",20},{"lisi",18},{"wangwu",30} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
void test2() {struct Stu arr[] = { {"zhangsan",20},{"lisi",18},{"wangwu",30} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}
void test1() {int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);Print(arr, sz);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);Print(arr, sz);
}
int main() {test1();test2();test3();return 0;
}
bubble_sort()函數只能排序為升序且不能改變,如何排為降序?
#include <stdio.h>
//如何不改變bubble_sort函數的情況下將數組排為降序
void Print(int arr[], int sz) {int i = 0;for (i = 0; i < sz; i++) {printf("%d ", arr[i]);}printf("\n");
}
void swap(char* buf1, char* buf2, size_t size) {int i = 0;for (i = 0; i < size; i++) {char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
void bubble_sort(void* base, size_t sz, size_t size, int (*cmp)(const void* e1, const void* e2)) {int i = 0;for (int i = 0; i < sz - 1; i++) {int j = 0;for (j = 0; j < sz - 1 - i; 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 cmp_int(const void* e1, const void* e2) {return *(int*)e2 - *(int*)e1;//后者比前者大時,交換兩元素
}
void test1() {int arr[] = { 0,1,2,3,4,5,6,7,8,9};int sz = sizeof(arr) / sizeof(arr[0]);Print(arr, sz);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);Print(arr, sz);
}
int main() {test1();return 0;
}
可以處理任意類型的編程——泛型編程