前言
-
指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
-
指针的大小是固定的4/8个字节(32位平台/64位平台)。
-
指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。
-
指针的运算。
1. 字符指针
在指针的类型中我们知道有一种指针类型为字符指针 char* ;
一般使用:
int main()
{
char ch = 'w';
printf("%c\n", ch);
char *pc = &ch;
*pc = 'e';
printf("%c\n", ch);
return 0;
}
int main()
{
const char* p = "abcdef";//把字符串首字符a的地址,赋值给了p
//char arr[] = "abcdef";
//*p = 'w'; 因为字符串"abcdef"是常量字符串 不能解引用之后被修改,因此用const来修饰 *p
printf("%s\n", p);
return 0;
}
思考打印的结果是什么
int main()
{
// 把字符串首字符a的地址,赋值给了p
// 常量字符串是不会被修改的,因此首元素的地址也是相同的
const char* p1 = "abcdef";
const char* p2 = "abcdef";
// 将"abcdef"用来初始化数组,其实就是将常量区的字符串拷贝了一份,再进行数组的初始化
// 因此两个数组首元素的地址是不一样的,因为它们是常量字符串的两份拷贝
char arr1[] = "abcdef";
char arr2[] = "abcdef";
if (p1 == p2)
printf("p1==p2\n");
else
printf("p1!=p2\n");
if (arr1 == arr2)
printf("arr1 == arr2\n");
else
printf("arr1 != arr2\n");
return 0;
}
2. 指针数组(存放指针的数组)
int* arr1[10]; //整型指针的数组
char* arr2[4]; //一级字符指针的数组
char** arr3[5]; //二级字符指针的数组
- 下面的代码可以很好的帮助我们理解指针数组
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* parr[3] = { arr1, arr2, arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
//*(p+i) --> p[i]
//printf("%d ", *(parr[i] + j)); 两种打印方法都可以,是等价的
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}
- 结合下面的图来分析上述代码
3. 数组指针(指向数组的指针)
3.1 数组指针的定义
数组指针 : 指向数组的指针
下面代码哪个是数组指针?
int* p1[10];
int (*p2)[10]; //p2的类型为 int(*)[10]
int *p1[10];
// 因为[]的优先级高于*,因此p1会先与[]结合,所以 p1[10]是一个数组,数组元素的类型为int*,因此为指针数组
int (*p)[10];
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组,数组每个元素为int类型。所以p是一个指针,指向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
3.2 &数组名VS数组名
数组名通常表示的都是数组首元素的地址
但是有2个例外:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
2. &数组名,这里的数组名表示的依然是整个数组,所以&数组名取出的是整个数组的地址
int main()
{
int arr[10] = {0};
// 数组首元素的地址
printf("%p\n", arr);
// 数组首元素的地址
printf("%p\n", &arr[0]);
// 整个数组的地址
printf("%p\n", &arr);
return 0;
}
- 下面这个代码可以区分arr和&arr的用法
int main()
{
int arr[10] = {0};
printf("%p\n", arr);
printf("%p\n", arr+1);
printf("%p\n", &arr[0]);
printf("%p\n", &arr[0]+1);
printf("%p\n", &arr);
printf("%p\n", &arr+1); //整个数组的地址+1,是跳过整个数组
return 0;
}
int main()
{
int arr[10] = {0};
// 数组指针,这个指针指向一个数组
int (*p)[10] = &arr;
return 0;
}
// p(指针变量)的类型为int(*)[10]; p里面存放的是整个数组的地址; p+1 指针是跳过整个数组;
// 其实*p也相当于数组名,也就是数组首元素的地址
int main()
{
char* arr[5] = {0};
char* (*pc)[5] = &arr;
char ch = 'w';
char* p1 = &ch;
char** p2 = &p1;
return 0;
}
3.3 数组指针的使用
- 先介绍一种不常用的数组指针的使用
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int (*p)[10] = &arr;
int i = 0;
// sz是数组元素的个数
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
// p是指向数组的,*p其实就相当于数组名,数组名又是数组首元素的地址
// 所以*p本质上是数组首元素的地址
printf("%d ", *(*p+i));
}
return 0;
}
- 上面的代码使用起来非常的别扭,不如直接使用下面的代码来打印
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
// p就是指针变量,存储的是数组首元素的地址
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
- 那么数组指针应该怎样来使用呢?请阅读以下代码
// int* p
// int arr[3][5],其实是一个二维数组的指针(接收的是二维数组首行的地址)
void print1(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++)
{
// arr[0] -> *(arr+0) 两者是等价的; arr[0]就是二维数组的首行地址
// arr+0是一个指针变量,它指向二维数组的首行地址, *(arr+0) 拿到首行元素的地址
// arr[i][j] -> *(*(arr+i)+j)
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
// int (*p)[5] 接收的是第一行元素的地址
void print2(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("%d ", p[i][j]); // p[i]就是第i行元素的首元素地址
}
printf("\n");
}
}
int main()
{
// arr是一个二维数组
int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
print2(arr, 3, 5);
return 0;
}
- 对上述传参,和打印参数的分析如下
- 学习了指针数组和数组指针,我们分析一下下面代码的意思
int arr[5]; // 整形数组arr
int *parr1[10]; // 整型的指针数组parr1
int (*parr2)[10]; // 数组指针parr2
// *说明parr2是一个指针; 去掉parr2,可以看出类型为:int (*)[10],说明parr2是一个指向数组的指针,且这个数组存放的元素类型为int
// 如下图所示
int (*parr3[10])[5]; // 存放数组指针的数组(int (*)[5] 是数组指针,parr3[10]是数组)
// 首先[]的优先级比*要高,因此parr3先于[10]结合,说明parr3[10]这是一个整体,是一个数组;parr3[10]前面有*,说明parr3[10]是一个指针数组
// 去掉parr3[10],可以看到int (*)[5],所以这个指针数组parr3[10],存放的指针指向的是一个数组,这个数组存放的元素类型为int
4. 数组参数、指针参数
4.1 一维数组传参
#include <stdio.h>
void test(int arr[])
{}
void test(int arr[10])
{}
void test(int *arr)
{}
// int arr[10] 传参对应 int arr[]
// int* arr2[20] 传参对应 int* arr2[20]
void test2(int* arr[20])
{}
void test2(int **arr)
{}
int main()
{
// 对于int arr[10],数组首元素的地址类型为int*
int arr[10] = {0}; //数组可以进行如上传参
// 对于int* arr2[20],数组首元素的地址类型为int**
int* arr2[20] = {0}; //指针数组可以进行如上传参
test(arr);
test2(arr2);
return 0;
}
4.2 二维数组传参
void test(int arr[3][5]) //正确传参
{}
void test(int arr[][]) //错误传参
{}
void test(int arr[][5]) //正确传参(二维数组,行可以省略,但是列不可以)
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
// 二维数组的数组名,表示的是首行的地址,也就是第一行元素的地址
// 第一行是一个有五个整型元素的一维数组,因此传参应该用数组指针来接收
// 第一行的类型为 int [5]
void test(int *arr) //错误传参
{}
void test(int* arr[5]) //错误传参
{}
// arr指向一个数组,这个数组有5个元素,元素类型为int
// arr就是二维数组的首行地址
// *(arr+0) 就是首行首元素的地址; *(*(arr+0)+0) 就是首行首元素
void test(int (*arr)[5]) //正确传参
{
printf("%p\t", arr);
printf("%p\t", *arr);
printf("%p\t", **arr);
}
void test(int **arr) //错误传参
{}
int main()
{
int arr[3][5] = {{1,2,3},{2,3,4},{4,5,6}};
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;
}
/*
思考:
当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
比如:
test1函数能接收什么参数?
*/
void test1(int *p)
{}
// 经过思考得
int main()
{
int arr[] = {1,2,3,4,5};
test1(arr);
int a = 10;
int* ptr = &a;
test(&a);
test1(ptr);
}
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(char **p)
{
}
int main()
{
char c = 'b';
char* pc = &c;
char** ppc = &pc;
char* arr[10];
test(&pc);
test(ppc);
//指针数组也传参,也可以用二级指针接收,
// 指针数组的每个元素都为char*, arr又为首元素的地址,所以数组首元素地址的类型就是char**,因此可以用二级指针来接收
test(arr);
return 0;
}
5. 函数指针(指向函数的指针)
- 对比数组和函数
int Add(int x, int y)
{
return x + y;
}
int main()
{
// 数组
int a = 10;
int* pa = &a;
*pa = 20;
printf("%d\n", *pa);
int arr[5] = {0};
//&数组名 就是 取出整个数组的地址
int (*p)[5] = &arr;//数组指针,这个指针p指向一个数组
// 函数
//&函数名 就是 取出的就是函数的地址呢?
printf("%p\n", &Add);
printf("%p\n", Add);
//对于函数来说,(&函数名)和(函数名)都是函数的地址(可以自己在编译器上运行)
// int (*pf)(int, int) = &Add;
// pf就是一个函数指针,指向一个函数,这个函数的范围值为int,两个参数的类型是(int, int)
int (*pf)(int, int) = Add;
// 取函数的地址,放入函数指针,
// *pf就是拿到函数
int ret = (*pf)(2, 3);
int ret = Add(2, 3);
int ret = pf(2, 3);
// *pf和pf都可以来调用Add函数;调用时(有无* 都可以);
// pf存放函数的地址,Add也就是函数的地址
// 因此Add(2, 3)就相当于pf(2, 3)
printf("%d\n", ret);
return 0;
}
- 阅读两段有趣的代码:
//代码1
( *(void (*)() )0 )();
注:void (*p)(); 其中p为函数指针, void (*)()为函数指针类型,这个函数指针指向的函数没有参数,返回值为void
0 看做是整型 类型为int
0 也可以看做是地址 0x00 00 00 00
因此,我们可以得出以下结论:
以上代码是一次函数的调用,调用的是函数地址为0的函数。
1.(void (*)())0; 就是将0强转为一个函数指针(指向的这个函数无参数,返回类型为void);
2.将(void (*)())0简化为函数指针p,那么 ( *(void (*)() )0 )() 就是(*p)()
// 综上,( *(void (*)() )0 )() 就是调用函数地址为0的函数指针
//代码2
void (* signal(int,void (*)(int)) )(int);
注: signal是函数名;
// 函数为signal(int , void (*)(int); 返回值为void (*)(int)
因此我们可以得出结论:以上代码是一次函数声明
1.声明的signal函数的第一个参数的类型是int,第二个参数的类型是函数指针void (*)(int),
且该函数指针指向的函数参数的类型为int,函数返回类型是void;
2.signal函数的返回类型是void (*)(int);
注:这个函数声明太复杂了,我们可以使用typedef将其简化如下
typedef void(* pf_t)(int) ; / /把void(*)(int)类型重命名为 pf_t
则原代码就相当于 pf_t signal(int, pf_t)
函数指针的用途
简单的写一个满足加法、减法、乘法、除法的计算器
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;
}
//计算
//回调函数
// 使用函数指针pf来调用,相对应的函数
void calc(int (*pf)(int , int))
{
int x = 0;
int y = 0;
int ret = 0;
printf("请输入2个操作数:>");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("%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;
}
- 思考 上述代码使用了函数指针 ,带来了那些便利?
由下图,我们可以得出结论,如果不使用函数指针,调用每一个函数的代码,都会有大量重复的代码。
进阶第二部分
6. 函数指针数组
数组是一个存放相同类型数据的存储空间,比如:
int* arr[10];
//数组的每个元素是int*
6.1 函数指针数组的定义
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
函数指针数组:存放函数指针的数组
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 (*pf)(int, int) = Add; //pf是函数指针
//arr就是函数指针的数组
int (*arr[4])(int, int) = { Add, Sub, Mul, Div };
//arr[4]数组中每个元素的类型为 int (*)(int,int)
int i = 0;
for (i = 0; i < 4; i++)
{
int ret = arr[i](8, 4); //调用函数指针数组中的函数
printf("%d \n", ret);
}
return 0;
}
- 附图一张,便于大家理解函数指针数组
6.2 函数指针数组的应用
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;
//函数指针的数组
//转移表
int (*pfArr[])(int, int) = {0, Add, Sub, Mul, Div};
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
if (input == 0)
{
printf("退出计算器\n");
}
else if (input >= 1 && input <= 4)
{
printf("请输入2个操作数:>");
scanf("%d %d", &x, &y);
ret = pfArr[input](x, y);//pfArr[1]:即调用函数指针数组中下标为1的函数
printf("%d\n", ret);
}
else
{
printf("选择错误\n");
}
} while (input);
return 0;
}
7. 指向函数指针数组的指针
int main()
{
//函数指针数组
int (*pfArr[])(int, int) = { 0, Add, Sub, Mul, Div };
//指向【函数指针数组】的指针
// (*ppfArr) 说明ppfArr是一个指针,指向函数指针数组int (* [5])(int, int)
int (*(*ppfArr)[5])(int, int) = &pfArr;
return 0;
}
8. 回调函数
1.回调函数就是一个通过函数指针调用的函数。
2.如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
3.回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
8.1 冒泡排序
9 8 7 6 5 4 3 2 1
第一趟之后就是: 8 7 6 5 4 3 2 1 9 // 第一趟进行8次比较,第二趟进行7次比较
第二趟之后就是: 7 6 5 4 3 2 1 8 9
// 9个数一共需要9趟
void bubble_sort(int arr[], int sz)
{
int i = 0;
//趟数
for (i = 0; i < sz - 1; i++)
{
//假设数组已经按照从小到大排好序了,这时flag为1
int flag = 1;
//一趟冒泡排序的过程
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;
// 说明此时还没有排序完成
flag = 0;
}
}
if (flag == 1)
{
break;
}
}
}
int main()
{
int arr[] = { 10,2,3,4,5,6,7,8,9 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
int i = 0;
while (sz)
{
printf("%d\t", *(arr + i));
i++;
sz--;
}
return 0;
}
8.2 qsort 函数
- 再使用qsort函数之前 我们需要了解如下的知识点
// c语言库中的快速排序接口
// void qsort(void* base,//你要排序的数据的起始位置
// size_t num,//待排序的数据元素的个数
// size_t width,//待排序的数据元素的大小(单位是字节)
// int(* cmp)(const void* e1, const void* e2)//函数指针-比较函数
// );
int main()
{
int a = 10;
//char* pa = &a;// int*和char*类型不同
void* pv = &a;//void*是无具体类型的指针,可以接受任意类型的地址
//void*是无具体类型的指针,所以不能解引用操作,也不能+-整数
return 0;
}
- 利用qsort来排序
//qsort -这个函数可以排序任意类型的数据
// void qsort(void* base,//你要排序的数据的起始位置
// size_t num,//待排序的数据元素的个数
// size_t width,//待排序的数据元素的大小(单位是字节)
// int(* cmp)(const void* e1, const void* e2)//函数指针-比较函数
// );
#include <stdlib.h>
//比较2个整型元素
//e1指向一个整数
//e2指向另外一个整数
// 第一种cmp_int 函数
int cmp_int(const void* e1, const void* e2)
{
// 根据compare函数的返回值制定的规则
if (*(int*)e1 < *(int*)e2) //因为void* 不能够被解引用,
return -1; //又因为已知比较数据的类型为int,所以强转为int*
else if (*(int*)e1 > *(int*)e2)
return 1;
else
return 0;
}
// 第二种cmp_int 函数(简洁并且和第一种的效果相同)
int cmp_int(const void* e1, const void* e2)
{
return (*(int*)e1 - *(int*)e2);
}
void test1()
{
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
//0 1 2 3 4 5 6 7 8 9
//把数组排成升序
int sz = sizeof(arr) / sizeof(arr[0]);
//bubble_sort(arr, sz);
qsort(arr, sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
8.2.1我们也可以利用qsort函数来对结构体排序
首先我们需要了解一个比较字符串的函数strcmp。
// 这是一个学生的结构体
struct Stu
{
char name[20];
int age;
};
// 1.可以通过每个学生的姓名来排序
// strcmp
// abbdef
// abbqwerttt (a的ASCILL值小于b,所以b比a大)
// 这两个字符串通过strcmp比较,首先是对两个字符串的首元素进行比较,其次第二个元素,以此类推;
int cmp_stu_by_name(const void* e1, const void* e2)
{
//strcmp --> (>0 ==0 <0)
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
// 2.也可以通过年龄来排序
int cmp_stu_by_age(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
void test2()
{
//测试使用qsort来排序结构数据
struct Stu s[] = { {"zhangsan", 15}, {"lisi", 30}, {"wangwu", 25} };
int sz = sizeof(s) / sizeof(s[0]);
//qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
int main()
{
//test1();
//test2();
return 0;
}
8.2.2 模仿qsort来改造我们自己写的bubble_sort
// void qsort(void* base,//你要排序的数据的起始位置
// size_t num,//待排序的数据元素的个数
// size_t width,//待排序的数据元素的大小(单位是字节)
// int(* cmp)(const void* e1, const void* e2)//函数指针-比较函数
// );
// 由qsort 得
// void bubble_sort(void*base, //你要排序的数据的起始位置
// int sz, //待排序的数据元素的个数
// int width, //待排序的数据元素的大小(单位是字节)
// int(*cmp)(const void*e1, const void*e2) /函数指针-比较函数
// );
//1. 当对数字进行比较时,调用 cmp_int
int cmp_int(const void* e1, const void* e2)
{
return (*(int*)e1 - *(int*)e2);
}
//2.当对字符串进行比较时,调用cmp_stu_by_name
int cmp_stu_by_name(const void* e1, const void* e2)
{
//strcmp --> (>0 ==0 <0)
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
// 交换函数
void Swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
// width: 传参对象的大小,单位为byte
// 由于并不知道使用者会传参什么类型的对象的地址,
// (注char* arr+1,每次跳过1byte; int* arr+1,每次跳过4byte)
// 1.因此(char*)base+j*width, (char*)base+(j+1)*width)分别是我们将比较或者将交换的两个对象的地址
// 2.char*类型,每次解引用只能解引用一个字节,因此对于任何类型的对象,我们都可以一个字节一个字节的交换。
// 所以我们需要将传递的对象的地址强制转化为char*类型
void bubble_sort(void* base, int sz, int width, int(*cmp)(const void* e1, const void*e2))
{
int i = 0;
//趟数
for (i = 0; i < sz - 1; i++)
{
int flag = 1;//假设数组是排好序
//一趟冒泡排序的过程
int j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
// cmp() 返回值如果大于0,说明e1 > e2
if (cmp((char*)base+j*width, (char*)base+(j+1)*width)>0)
{
// 使用交换函数,交换两个元素的位置
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
flag = 0;
}
}
if (flag == 1)
{
break;
}
}
}
// 对数组进行排序
void test3()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
//0 1 2 3 4 5 6 7 8 9
//把数组排成升序
int sz = sizeof(arr) / sizeof(arr[0]);
//bubble_sort(arr, sz);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
// 对结构体进行排序
void test4()
{
//测试使用qsort来排序结构数据
struct Stu s[] = { {"zhangsan", 15}, {"lisi", 30}, {"wangwu", 25} };
int sz = sizeof(s) / sizeof(s[0]);
bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
//bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
int main()
{
test3();
test4();
return 0;
}