目录
一. 字符指针
1.字符指针的定义
2.字符指针的用法
3.字符指针练习
二. 数组指针
1.指针数组的定义
2.指针数组的用法
三. 指针数组
1.数组指针的定义
2.数组名和&数组名的区别
3.数组指针的用法
4.练习
四. 数组传参和指针传参
1.一维数组传参
2.二维数组传参
3.一级指针传参
4.二级指针传参
五. 函数指针
1.函数指针的定义
2.取函数指针的地址
3.函数指针的用法
4.练习
六. 函数指针数组
1.函数指针数组的定义
2.函数指针数组的用法
七. 指向函数指针数组的指针
1.指向函数指针数组的指针的定义
2.指针总结
八. 回调函数
1.回调函数的概念
2.回调函数的例子
3.qsort函数
4.模拟实现冒泡排序版qsort函数
前言:
指针的主题,我们在初级阶段的《六.初阶指针_殿下p的博客-CSDN博客》章节已经接触过了,我们知道了指针的概念:
1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
4. 指针的运算。
这个章节,我们继续探讨指针的高级主题。
一. 字符指针
1.字符指针的定义
定义:字符指针,常量字符串,存储时仅存储一份(为了节约内存)
char *pa="string";
2.字符指针的用法
用法:
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
对于指向字符串的字符指针:
int main()
{
char* pstr = "hello world";
printf("%s\n", pstr);
return 0;
}
//打印结果为:hello world
上面代码 char* pstr = " hello world " 特别容易让人以为是把 hello world 放在字符指针 pstr 里了,但是本质上是把字符串 hello world 首字符的地址放到了 pstr 中。
3.字符指针练习
下面代码将输出什么结果呢?
int main()
{
char str1[] = "abcdef";
char str2[] = "abcdef";
const char* str3 = "abcdef";
const char* str4 = "abcdef";
if (str1 == str2)
printf("str1 == str2\n");
else
printf("str1 != str2\n");
if (str3 == str4)
printf("str3 == str4\n");
else
printf("str3 != str4\n");
return 0;
}
运行结果:
这是因为:
(1)str1和str2是数组,在内存中开辟两块内存空间,这两块内存空间的起始地址不相同,这两个值自然不一样。
(2)“abcdef”是常量字符串,本身不可以被修改,在内存中这个常量字符串只开辟一块内存空间,str3和str4是两个字符指针,这两个指针都指向该字符串的首字符地址,所以str3和str4相等。
二. 数组指针
1.指针数组的定义
定义:指针数组是数组,数组中存放的是指针(地址)
int arr1[10]; //整型数组
char arr2[5]; //字符数组
int *parr1[10]; //存放整形指针的数组--指针数组
char *parr2[5]; //存放字符指针的数组--指针数组
注:[] 优先级高于*,会先与 p 结合成为一个数组,再由 int* 说明这是一个整型指针数组。
2.指针数组的用法
#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* p[] = { arr1, arr2, arr3 }; // 首元素地址
int i = 0;
for(i=0; i<3; i++) {
int j = 0;
for(j=0; j<5; j++) {
printf("%d ", *(p[i] + j)); // p[i]表示遍历指针数组内的每一个指针
//*(p[i]+j)则表示每一个指针向后移动j个元素所指向的元素
// == p[i][j]
}
printf("\n");
}
return 0;
}
如下图所示:
三. 指针数组
1.数组指针的定义
定义:数组指针是指针,是指向数组的指针。
整形指针 - 是指向整型的指针
字符指针 - 是指向字符的指针
数组指针 - 是指向数组的指针
int arr[n];
int (*p)[n]=&arr; //数组指针
前面提到过,[ ]的优先级高于*,所以加上括号,p先和*结合,说明p是一个指针变量,然后指向的是一个大小为n个整型的数组,所以p是一个指针,指向一个数组,叫数组指针。
2.数组名和&数组名的区别
先观察如下代码:
int main()
{
int arr[10] = {0};
printf("%p\n", arr);
printf("%p\n", &arr);
return 0;
}
发现它们的地址是一样的,但其实:
arr和&arr的值一样,但含义却不一样:
int main()
{
int arr[10] = { 0 };
int* p1 = arr;
int(*p2)[10] = &arr;
printf("%p\n", p1);
printf("%p\n", p1 + 1);
printf("%p\n", p2);
printf("%p\n", p2 + 1);
return 0;
}
我们发现arr+1跳过一个整形,而&arr+1跳过一个数组。
这是因为,arr表示数组首元素的地址,&arr表示整个数组的地址。
总结:
数组名是数组首元素的地址,但是有 2 个 例外:
① sizeof ( 数组名 ) - 数组名表示整个数组,计算的是整个数组的大小,单位是字节。
② &数组名 - 数组名表示整个数组,取出的是整个数组的地址。
3.数组指针的用法
二维数组以上常使用数组指针:
void print1 (int arr[3][5], int row, int col)
{
int i = 0;
int j = 0;
for(i=0; i<row; i++) {
for(j=0; j<col; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print2 (int(*p)[5], int row, int col)
{
int i = 0;
int j = 0;
for(i=0; i<row; i++) {
for(j=0; j<col; j++) {
printf("%d ", *(*(p + i) + j));
// printf("%d ", (*(p + i))[j]);
// printf("%d ", *(p[i] + 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}};
// print1(arr, 3, 5);
print2(arr, 3, 5); // arr数组名,表示数组首元素的地址
return 0;
}
4.练习
分析下以下代码的含义:
int arr[5];
int* parr1[10];
int (*parr2)[10];
int (*parr3[10])[5];
解析:
//arr是一个有5个元素的整型数组
int arr[5];
//parr1是一个指针数组,数组有10个元素,每个元素都是int*的指针
int* parr1[10];
//parr2是一个数组指针,该指针指向一个数组,数组有10个元素,每个元素都是int型
int (*parr2)[10];
//parr3是一个数组指针数组,该数组存放10个数组指针,
//每个数组指针能够指向一个数组,数组有5个元素,每个元素的类型为int型
int (*parr3[10])[5];
四. 数组传参和指针传参
1.一维数组传参
判断下面函数的形参是否合理:
void test(int arr[]) //ok?
{}
void test(int arr[10]) //ok?
{}
void test(int *arr) //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);
}
答:以上都合理
2.二维数组传参
判断以下函数的参数是否合理:
void test(int arr[3][5]) //0k?
{}
void test(int arr[][5]) //0k?
{}
void test(int arr[3][]) //ok?
{}
void test(int arr[][]) //ok?
{}
void test(int* arr) //ok?
{}
void test(int* arr[5]) //ok?
{}
void test(int(*arr)[5]) //ok?
{}
void test(int** arr) //ok?
{}
int main()
{
int arr[3][5] = {0};
test(arr); // 二维数组传参
return 0;
}
答:只有第一,第二和第七个合理,其他都不行,理由如下:
//直接数组传参
void test(int arr[3][5])//可以
{}
//数组传参,行可以省略
void test(int arr[][5]) //可以
{}
//数组传参,列不可以省略
void test(int arr[3][]) //不可以
{}
//同上
void test(int arr[][]) //不可以
{}
//数组名表示数组首元素的地址,二维数组的首元素为第一行一维数组的地址,
//需要一个指向数组的数组指针来接受,一个一阶指针接受不下
void test(int* arr) //不可以
{}
//参数部分为指针数组,不是指针,无法接受二维数组的首元素地址
void test(int* arr[5]) //不可以
{}
//数组指针,该指针指向的数组有5个元素,可以接受二位数组的首地址
void test(int(*arr)[5]) //可以
{}
//二维数组名表示第一行数组的地址,二级指针容纳不下
void test(int** arr) //不可以
{}
int main()
{
int arr[3][5] = {0};
test(arr); // 二维数组传参
return 0;
}
3.一级指针传参
例:
void print(int* ptr, int sz) // 一级指针传参,用一级指针接收
{
int i = 0;
for(i=0; i<sz; i++) {
printf("%d ", *(ptr + i));
}
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
// p是一级指针,传给函数
print(p, sz);
return 0;
}
那么问题来了,当函数参数为一级指针时,可以接收什么样的参数呢?
如下所示:
void test1(int* p)
{}
int main()
{
int a = 10;
int* pa = &a;
test1(&a);
test1(pa);
return 0;
}
4.二级指针传参
例:
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int* p = &n;
int** pp = &p;
test(pp);
test(&p); // 取p指针的地址,依然是个二级指针
return 0;
}
还是那个问题,当函数的参数为二级指针时,可以接收什么样的参数呢?
如下所示:
void test(int **p) // 二级指针
{
;
}
int main()
{
int *ptr;
int** pp = &ptr;
test(&ptr); // 传一级指针变量的地址
test(pp); // 传二级指针变量
int* arr[10]; //指针数组
test(arr); // 传存放一级指针的数组,因为arr是首元素地址,元素类型为int*
return 0;
}
五. 函数指针
1.函数指针的定义
定义:指向函数的指针,存放函数地址的指针
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pf)(int, int) = &Add; //pf是一个函数指针
return 0;
}
int ( * pf ) (int ,int ) = & Add;
解释:*先于pf结合,表示pf是一个指针,然后pf指向一个函数,括号内表示该函数有两个参数,参数类型都为int ,对于函数来说还有返回类型,最前面的int表示函数的返回类型
2.取函数指针的地址
函数也是有地址的,取函数地址可以通过 &函数名 或者 函数名 实现。
但是,要注意:
- 函数名 == &函数名 (两者的含义是一样的,都表示函数的地址)
- 数组名 != &数组名
如下所示:
int Add(int x, int y)
{
return x + y;
}
int main()
{
// 函数指针 - 存放函数地址的指针
// &函数名 - 取到的就是函数的地址
printf("%p\n", &Add);
printf("%p\n", Add);
return 0;
}
地址是一样的:
3.函数指针的用法
例:
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pf)(int, int) = &Add; //创建函数指针,指向Add函数
int ret = (*pf)(3, 5); // 对 pf 进行解引用操作,找到它所指向的函数,然后对其传参
printf("%d\n", ret);
return 0;
}
那么能不能把(*)pf(3,5)写成*pf(3,5)呢?
答案是不可以的,因为这么写会导致*对函数返回值进行解引用,所以星号一定要用括号括起来。
当然也可以选择不写*,因为前文提到过,函数名和&函数名都表示函数的地址:
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pf)(int, int) = &Add;
// int ret = Add(3, 5);
int ret = pf(3, 5);
printf("%d\n", ret);
return 0;
}
结果是一样的,说明(*pf)前的*加不加都可以。
4.练习
(1)例一:
(*(void (*)())0)();
解析:这段代码的作用其实是调用 0 地址处的函数,该函数无参,返回类型是 void
如下图所示:
(2)例二:
void (*signal(int, void(*)(int)))(int);
解析:这段代码是对函数的声明
如下图所示:
1.signal先与()结合,说明signal是函数
2.signal函数的第一个参数类型是int,第二个参数类型是函数指针,该函数指针,指向一个参数为int,返回类型是void的函数。
3.signal函数的返回类型也是一个函数指针,该函数指针,指向一个参数为int,返回类型为void的函数。
上面的函数声明看上去过于冗杂,一眼让人难以察觉这段代码的真正含义,我们可以做如下简化:
int main()
{
void (* signal(int, void(*)(int)) )(int);
typedef void(*pfun_t)(int); // 对void(*)(int)的函数指针类型重命名为pfun_t
pfun_t signal(int, pfun_t); // 和上面的写法完全等价
return 0;
}
用typedef对重复出现的函数指针进行重命名,这样该函数声明就一目了然了。
六. 函数指针数组
1.函数指针数组的定义
定义:如果要把函数的地址存到一个数组中,那这个数组就叫函数指针数组。
int Add(int x, int y) {
return x + y;
}
int Sub(int x, int y) {
return x - y;
}
int main()
{
int (*pf)(int, int) = Add;
int (*pf2)(int, int) = Sub;
int (*pfArr[2])(int, int) = {Add, Sub}; //函数指针数组,数组元素为函数的地址
return 0;
}
2.函数指针数组的用法
引例:实现一个计算器,可以进行简单的加减乘除运算。
代码1:
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;
do {
menu();
int x = 0;
int y = 0;
int ret = 0;
printf("请选择:> ");
scanf("%d", &input);
switch(input) {
case 1:
printf("请输入2个操作数:> ");
scanf("%d %d", &x, &y);
ret = Add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("请输入2个操作数:> ");
scanf("%d %d", &x, &y);
ret = Div(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("请输入2个操作数:> ");
scanf("%d %d", &x, &y);
ret = Mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("请输入2个操作数:> ");
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;
}
运行结果:
当前代码看着主要功能都实现了,但是还有很多可以优化的地方:
- 当前代码冗余,存在大量重复出现的语句。
- 添加计算器的功能(比如 a & b,a^b.....)时每加一个功能都要写一段case,每写一段case都会使代码显得很长,很没技术含量,能否更方便地增加?
这时候使用函数指针数组就会方便很多:
#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;
do {
menu();
// pfArr 就是函数指针数组
int (*pfArr[5])(int, int) = {NULL, Add, Sub, Mul, Div};
int x = 0;
int y = 0;
int ret = 0;
printf("请选择:> ");
scanf("%d", &input);
if(input >= 1 && input <= 4) {
printf("请输入2个操作数:> ");
scanf("%d %d", &x, &y);
ret = (pfArr[input])(x, y);
printf("ret = %d\n", ret);
}
else if(input == 0) {
printf("退出程序\n");
break;
} else {
printf("选择错误\n");
}
} while(input);
return 0;
}
这就是函数指针数组的应用。接收一个下标,通过下标找到数组里的某个元素,这个元素如果恰好是一个函数的地址,就会去调用那个函数。它做到了一个 "跳板" 的作用,所以我们通常称这种数组叫做 转移表 。
七. 指向函数指针数组的指针
1.指向函数指针数组的指针的定义
定义:指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素是函数指针。
int Add(int x, int y) {
return x + y;
}
int main()
{
int arr[10] = {0};
int (*p)[10] = &arr; // 取出数组的地址
int (*pfArr[4])(int, int); // pfArr是一个数组 - 函数指针的数组
int (* (*ppfArr)[4])(int, int) = &pfArr;
// ppfArr 是一个指针,指针指向的数组有4个元素
// 每个元素的类型是一个函数指针 int(*)(int, int)
return 0;
}
2.指针总结
void add(int,int); //函数
int arr[10]; //数组
int *prr[10]; //指针数组
int (*pa)[10]; //数组指针
int (*padd)(int,int)=add; //函数指针
int (*parr[10])(int,int); //函数指针数组
int (*(*pparr)[10])(int,int)=&parr; //指向函数指针的数组
八. 回调函数
1.回调函数的概念
回调函数是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时候,我们就称之为回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的时间或条件发生时由另外的一方调用的,用于该事件或条件进行响应。
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;
}
void Calc(int (*pf)(int, int))
{
int x = 0;
int y = 0;
printf("请输入2个操作数:>");
scanf("%d %d", &x, &y);
printf("%d\n", pf(x, y));
}
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;
}
解析:
3.qsort函数
定义:qsort 函数是C语言编译器函数库自带的排序函数( 需引入头文件 stdlib.h )
#include <stdlib.h>
void qsort(
void* base, //待排序数组的首元素
size_t num, //待排序数组的元素个数
size_t size, //待排序数组的每个元素的大小,单位是字节
int (*compar)(const void*, const void*) //可以实现 比较待排序数据大小的 函数
);
让我们回顾下冒泡排序:
#include <stdio.h>
void bubble_sort (int arr[], int sz)
{
int i = 0;
// 确认趟数
for (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;
}
}
}
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[10] = {9,8,7,6,5,4,3,2,1,0};
int sz = sizeof(arr) / sizeof(arr[0]);
print_arr(arr, sz);
bubble_sort(arr, sz);
print_arr(arr, sz);
return 0;
}
会发现,这个冒泡排序只能实现整形数据的排序,当我们想要对字符串或者结构体排序时,这个冒泡排序就显得有些寒酸了,而qsort函数可以帮助我们实现任意数据类型的排序:
qsort 整型数据排序(升序):
#include <stdio.h>
#include <stdlib.h>
/*
void qsort (
void* base,
size_t num,
size_t size,
int (*cmp_int)(const void* e1, const void* e2)
);
*/
int cmp_int(const void* e1, const void* e2)
{
// 升序: e1 - e2
return *(int*)e1 - *(int*)e2;
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
void int_sort()
{
int arr[] = {9,8,7,6,5,4,3,2,1,0};
int sz = sizeof(arr) / sizeof(arr[0]);
// 排序(分别填上四个参数)
qsort(arr, sz, sizeof(arr[0]), cmp_int);
// 打印
print_arr(arr, sz);
}
int main()
{
int_sort();
return 0;
}
运行结果为:0 1 2 3 4 5 6 7 8 9
qsort 对结构体排序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Stu
{
char name[20];
int age;
};
/*
void qsort (
void* base,
size_t num,
size_t size,
int (*cmp_int)(const void* e1, const void* e2)
);
*/
//按照年龄来排序
int cmp_struct_age(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//按照名字,也就是字符串来排序
int cmp_struct_name(const void* e1, const void* e2)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void struct_sort()
{
// 使用qsort函数排序结构体数据
struct Stu s[3] = {
{"Ashe", 39},
{"Hanzo", 38},
{"Ana", 60}
};
int sz = sizeof(s) / sizeof(s[0]);
// 按照年龄排序
qsort(s, sz, sizeof(s[0]), cmp_struct_age);
// 按照名字来排序
qsort(s, sz, sizeof(s[0]), cmp_struct_name);
}
int main()
{
struct_sort();
return 0;
}
运行结果:
按照年龄排序:
按照姓名(字符串)排序:
4.模拟实现冒泡排序版qsort函数
模拟qsort实现一个冒泡排序版本的通用排序算法:
#include <stdio.h>
#include <string.h>
struct Stu
{
char name[20];
char age;
};
// 模仿qsort实现一个冒泡排序的通用算法
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++;
}
}
void bubble_sort_q (
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 j = 0;
for(j=0; j<sz-1-i; j++) {
// 两个元素比较 arr[i] arr[j+i]
if(cmp( (char*)base+j*width, (char*)base+(j+1)*width ) > 0) {
//交换
Swap((char*)base+j*width, (char*)base+(j+1)*width, width);
}
}
}
}
int cmp_struct_age(const void* e1, const void* e2) {
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int cmp_struct_name(const void* e1, const void* e2) {
return strcmp( ((struct Stu*)e1)->name, ((struct Stu*)e2)->name );
}
void struct_sort()
{
// 使用qsort排序结构体数据
struct Stu s[] = {"Ashe", 39, "Hanzo", 38, "Ana", 60};
int sz = sizeof(s) / sizeof(s[0]);
// 按照年龄排序
bubble_sort_q(s, sz, sizeof(s[0]), cmp_struct_age);
// 按照名字排序
bubble_sort_q(s, sz, sizeof(s[0]), cmp_struct_name);
}
void print_arr(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) {
// 升序: e1 - e2
return *(int*)e1 - *(int*)e2;
}
void int_sort()
{
int arr[] = {9,8,7,6,5,4,3,2,1,0};
int sz = sizeof(arr) / sizeof(arr[0]);
// 排序
bubble_sort_q(arr, sz, sizeof(arr[0]), cmp_int);
// 打印
print_arr(arr, sz);
}
int main()
{
int_sort();
// struct_sort();
return 0;
}
本篇到此结束,码文不易,还请多多支持哦!