【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序)(下)

简介: 【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序)(下)

8、回调函数

1.定义

回调函数就是一个通过函数指针调用的函数。

理解:如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这个函数是回调函数。
特点:回调函数不是由该函数的实现方直接调用(其实也就是回调函数自身),而是在特定的事件或条件发生时由另外的一方调用的(另一个函数调用),用于对该事件或条件进行响应。

举例:比如上面计算器的方法二实现方式就是利用了回调函数的方法。

//计算器
#include <stdio.h>
void menu()
{
    printf("**************************\n");
    printf("**  1. add       2. sub **\n");
    printf("**  3. mul       4. div **\n");
    printf("**  5. xor       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 Xor(int x, int y)
{
    return x ^ y;
}
void Calc(int (*pf)(int, int))//Calc就是一个回调函数
{
    int x = 0;
    int y = 0;
    printf("请输入两个操作数:>");
    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");
            ;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);
}


冒泡排序复习回顾: 整型数组排序。那我们将这样使 用函数:
冒泡排序的思想:两两相邻的元素进行比较,并且可能的话需要交换。

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) _qsor函数_17

一趟解决一个数字的排序问题,

第一趟最大值9出现到最右侧,

第二趟8到右侧第二位 10个数字,需要进行9躺冒泡排序

n个数字 n - 1躺

一趟冒泡排序内部:

第一趟:10个数字待排序,9对比较

第二趟:9个数字待排序,8对比较

第三趟:8个数字待排序,7对比较 ……

第九趟:2个数字待排序,1对比较

#include<stdio.h>
void print(int arr[], int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
void bubble_sort(int arr[], int sz)
{
    int i = 0;
    int j = 0;

for (i = 0; i < sz - 1; i++)//确定冒泡排序的趟数
    {
        int flag = 1;//用于判断比较是否继续
        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[] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    printf("排序前:");
    print(arr, sz);
    //冒泡排序,进行升序排列
    bubble_sort(arr, sz);
    printf("排序后:");
    print(arr, sz);
    return 0;
}

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) _回调函数_18

上面我们写的冒泡排序只能排整数,如果遇到浮点型、结构体或者其他类型,那么上面的冒泡排序函数就不能使用了。


2.qsort函数

在C语言的库函数中有一个用于任意类型排序的函数 qsort

qsort-- - 使用快速排序的方法

qsort相关信息:

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) _qsort函数排序_19

void qsort(void* base,//待排序目标数组的起始位置
    size_t num,//数组的元素个数
    size_t width,//每个元素占用的字节数
    int(__cdecl * compare)(const void* elem1, const void* elem2)
    //函数指针,排序所使用的比较函数
    //不同类型的数据比较的方法是不一样的,所以需要将比较方法写成函数传递给qsort
   //简化:int(* cmp)(const void* elem1, const void* elem2) 
   //函数指针,指针指向的参数有两个,参数类型都是const void*,函数返回类型是int

void* 类型讲解: void是无,空的含义,void * 表示的是无类型的指针。 void* 指针可以接收任意类型的地址(void 可以看作指针万能筒,可以接收任意类型) void 类型指针不能进行解引用操作的。

理解:我们知道指针的类型决定了指针解引用操作时访问字节的个数,如果指针类型是void *,无类型指针,那么就无法得知该指针解引用访问多少个字节数。

void* 类型指针不能进行++ / ±整数运算的操作。

理解:我们知道指针类型的意义除了决定指针解引用时访问字节的个数外,还决定了指针 + -整数运算的步长,如果指针类型是void *
无类型指针,那么就无法得知该指针 + -的步长到底是多少个字节。

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) _函数指针_20


① qsort排序整型数组

qsort函数中为了排序任意类型的数据,所以参数均给了void *
(1)下面我们来试着用qsort排序一下整型数组,熟悉qsort的使用:

#include<stdio.h>
#include<stdlib.h>
    //为了调用qsort,需要将比较函数传给qsort
    //按照qsort函数参数的类型,写出需要使用的比较函数
    int cmp_int(const void* elem1, const void* elem2)
{

    return *(int*)elem1 - *(int*)elem2;
}
void test1()
{
    int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    int i = 0;
    qsort(arr, sz, sizeof(arr[0]), cmp_int);
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
}
int main()
{
    test1();//排序整型数组并打印
    return 0;
}

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) _函数指针_21


② qsort排序浮点型数组

(2)下面再用qsort排序一下浮点型型数组,继续熟悉qsort的使用方式:

#include<stdio.h>
#include<stdlib.h>
int cmp_float(const void* e1, const void* e2)
{
    方法一:判断大小,返回整数
    //if (*(float*)e1 == *(float*)e2)
    //{
    //    return 0;
    //}
    //else if (*(float*)e1 - *(float*)e2 > 0)
    //{
    //    return 1;
    //}
    //else
    //{
    //    return -1;
    //}

    //方法二:强制类型转换成int类型
    return (int)(*(float*)e1 - *(float*)e2);
}
void test2()
{
    float arr2[] = { 4.0, 5.0, 1.0, 2.0, 3.0, 9.0 };
    int sz = sizeof(arr2) / sizeof(arr2[0]);
    int i = 0;
    qsort(arr2, sz, sizeof(arr2[0]), cmp_float);
    for (i = 0; i < sz; i++)
    {
        printf("%lf ", arr2[i]);
    }
}
int main()
{
    test2();//排序浮点型数组并打印
    return 0;
}

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) _回调函数_22


③ qsort排序结构体

(3)利用qsort排序结构体数据

#include<stdio.h>
#include<stdlib.h>
#include<string.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;
}
int cmp_Stu_by_name(const void* e1, const void* e2)
{
    //字符串比较需要用到字符串比较库函数strcmp
    return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void test3()
{
    struct Stu s[] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} };
    int sz = sizeof(s) / sizeof(s[0]);
    int i = 0;
    qsort(s, sz, sizeof(s[0]), cmp_Stu_by_age);
    printf("按年龄排序:\n");
    for (i = 0; i < sz; i++)
    {
        printf("%s,%d\n", s[i].name, s[i].age);
    }
    qsort(s, sz, sizeof(s[0]), cmp_Stu_by_name);
    printf("按名字排序:\n");
    for (i = 0; i < sz; i++)
    {
        printf("%s,%d\n", s[i].name, s[i].age);
    }
}
int main()
{
    test3();//排序结构体数组并打印
    return 0;
}

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) _qsort函数排序_23


④ qsort排序任意类型数组

(4)模拟实现qsort排序任意类型的排序函数-- - Bubble_sort

#include<stdio.h>
#include<string.h>
int cmp_int(const void* elem1, const void* elem2)
{
    return (*(int*)elem1 - *(int*)elem2);
}
void Swap(char* e1, char* e2, int width)
{
    int i = 0;
    //逐个字节交换
    for (i = 0; i < width; i++)
    {
        //方法一:
        //char tmp = *(e1 + i);
        //*(e1 + i) = *(e2 + i);
        //*(e2 + i) = tmp;
        //方法二:
        char tmp = *e1;
        *e1 = *e2;
        *e2 = tmp;
        e1++;
        e2++;
    }
}

void Bubble_sort(void* base, int num, int width, int (*cmp)(void* e1, void* e2))
{
    int i = 0;//size_t就是unsigned int无符号整型的类型重定义,这里可以直接用int类型
    //确定排序的趟数
    for (i = 0; i < num - 1; i++)
    {
        int j = 0;
        //确定每趟排序的对数
        for (j = 0; j < num - 1 - i; j++)
        {
            //比较大小
            if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
            {
                //元素位置交换
                Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
            }
        }
    }
}

//void qsort(void* base,//待排序目标数组的起始位置
//           size_t num,//数组的元素个数
//           size_t width,//每个元素占用的字节数
//           int(__cdecl* compare)(const void* elem1, const void* elem2)//函数指针,
//排序所使用的比较函数
//   //简化:int(* cmp)(const void* elem1, const void* elem2) 
//   //函数指针,指针指向的参数有两个,参数类型都是const void*,函数返回类型是int
//   //不同类型的数据比较的方法是不一样的,所以需要将比较方法写成函数传递给qsort
//           );

void test4()
{
    int arr[] = { 1,3,5,7,9,2,4,6,8,10 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    int i = 0;
    Bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
}

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;
}
int cmp_Stu_by_name(const void* e1, const void* e2)
{
    //字符串比较需要用到字符串比较库函数strcmp
    return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

void test3()
{
    struct Stu s[] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} };
    int sz = sizeof(s) / sizeof(s[0]);
    int i = 0;
    qsort(s, sz, sizeof(s[0]), cmp_Stu_by_age);
    printf("按年龄排序:\n");
    for (i = 0; i < sz; i++)
    {
        printf("%s,%d\n", s[i].name, s[i].age);
    }
    Bubble_sort(s, sz, sizeof(s[0]), cmp_Stu_by_name);
    printf("按名字排序:\n");
    for (i = 0; i < sz; i++)
    {
        printf("%s,%d\n", s[i].name, s[i].age);
    }
}
int main()
{
    test3();//调用模拟排序函数排序struct Stu类型数据并打印
    test4();//调用模拟排序函数排序int类型数据并打印
    return 0;
}

【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) _回调函数_24


【C语言进阶】——指针(二) (函数指针,回调函数,qsort排序) _函数指针数组_25

目录
相关文章
|
5月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
479 13
|
6月前
魔法指针 之 函数指针 回调函数
魔法指针 之 函数指针 回调函数
31 0
|
6月前
|
C++
指针中的回调函数与qsort的深度理解与模拟
本文详细介绍了回调函数的概念及其在计算器简化中的应用,以及C++标准库函数qsort的原理和使用示例,包括冒泡排序的模拟实现。
43 1
|
6月前
|
存储
一篇文章了解区分指针数组,数组指针,函数指针,链表。
一篇文章了解区分指针数组,数组指针,函数指针,链表。
46 0
|
6月前
|
C语言
无头链表二级指针方式实现(C语言描述)
本文介绍了如何在C语言中使用二级指针实现无头链表,并提供了创建节点、插入、删除、查找、销毁链表等操作的函数实现,以及一个示例程序来演示这些操作。
81 0
|
6月前
|
算法 搜索推荐 C语言
【C语言篇】深入理解指针4(模拟实现qsort函数)
【C语言篇】深入理解指针4(模拟实现qsort函数)
44 2
|
7月前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
265 4
|
8月前
|
存储 程序员 C语言
指针的高级应用:指针数组、数组指针、函数指针等。
指针的高级应用:指针数组、数组指针、函数指针等。
|
8月前
|
C语言
【C初阶——指针5】鹏哥C语言系列文章,基本语法知识全面讲解——指针(5)
【C初阶——指针5】鹏哥C语言系列文章,基本语法知识全面讲解——指针(5)
|
8月前
|
C语言
【C初阶——指针4】鹏哥C语言系列文章,基本语法知识全面讲解——指针(4)
【C初阶——指针4】鹏哥C语言系列文章,基本语法知识全面讲解——指针(4)