目录
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;
}
可以处理任意类型的编程——泛型编程