当前位置: 首页 > article >正文

C_深入理解指针(四) —— 回调函数、qsort使用举例、qsort函数的模拟实现

目录

一、回调函数

二、qsort 使用举例

1、使用qsort函数排序整型数据

2、使用qsort排序结构数据

三、qsort函数的模拟实现


一、回调函数

  1. 回调函数是⼀个通过函数指针调用的函数
  2. 若把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。
  3. 回调函数不是由该函数的实现方式直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进⾏响应。
        上一篇博客中写的计算机实现的代码中,红色框中的代码是重复出现的,其中虽然执行计算的逻辑是区别的,但是输⼊输出操作是冗余的,这就需要qsort函数

        因为红色框中的代码,只有调用函数的逻辑是有差异的, 回调函数的功能: 可把调用的函数的地址以参数的形式传递过去,使用函数指针接收,函数指针指向什么函数就调用什么函数

使用回到函数改造前: 

//使⽤回调函数改造前
#include <stdio.h>

int add(int a, int b)
{
    return a + b;
}

int sub(int a, int b)
{
    return a - b;
}

int mul(int a, int b)
{
    return a * b;
}

int div(int a, int b)
{
    return a / b;
}

int main()
{
    int x, y;
    int input = 1;
    int ret = 0;
    do
    {
        printf("*************************\n");
        printf(" 1:add  2:sub \n");
        printf(" 3:mul  4:div \n");
        printf("*************************\n");
        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>
int add(int a, int b)
{
    return a + b;
}

int sub(int a, int b)
{
    return a - b;
}

int mul(int a, int b)
{
    return a * b;
}

int div(int a, int b)
{
    return a / b;
}

void calc(int(*pf)(int, int))
{
    int ret = 0;
    int x, y;
    printf("输⼊操作数:");
    scanf("%d %d", &x, &y);
    ret = pf(x, y);
    printf("ret = %d\n", ret);
}

int main()
{
    int input = 1;
    do
    {
        printf("*************************\n");
        printf(" 1:add  2:sub \n");
        printf(" 3:mul  4:div \n");
        printf("*************************\n");
        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;
}

总结:

  1.  qsort是C语言中的一个库函数,这个函数是用来对数据进行排序,对任意类型的数据都能进行排序
  2. qsort函数的头文件是#include<stdlib>
  3. qsort函数的底层逻辑是用了快速排序的思想,而且qsort排序默认是升序

        可以通过第一个形参base知道所指向的数据的地址位置,可以通过第三个形参size的大小去判断比较的类型:

void qsort (void* base, //指向待排序数组的第一个元素的指针
            size_t num,//base指向数组中的元素个数 
            size_t size,//base指向的数组中一个元素的大小,单位是字节
            int (*compar)(const void*,const void*)//函数指针——传递函数的地址
            );

        以下代码排序整型数据是没问题的,但是对于其他类型的数据,比如字符型、字符串、浮点数、结构体等类型的数据则不能排序。比如两个字符串比较大小不能使用>、>=、<、<=、==、!=,两者想要比较的话则需要用strcmp(比较对应位置上字符元素的ASCII码值的大小)

排序算法有很多种:冒泡排序、插入排序、选择排序、快速排序等等......

#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]);
    }
}

int main()
{
    int arr[] = { 3,1,7,9,4,2,6,5,8,0 };
    //排序——升序
    int sz = sizeof(arr)/sizeof(arr[0]);
    bubble_sort(arr, sz);
    Print_arr(arr,sz);
    return 0;
}

二、qsort 使用举例

1、使用qsort函数排序整型数据

#include <stdio.h>
//qosrt函数的使⽤者得实现⼀个⽐较函数
//void类型不能解引用
int int_cmp(const void * p1, const void * p2)
{
    //所以首先要强制转化为整型类型再解引用
    if (*( int *)p1 - *(int *) p2)
        return 1;
    else if (*( int *)p1 - *(int *) p2)
        return -1;
    else
        return 0;
}

int main()
{
    int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
    int i = 0;
 
    qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
    for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)    
    {
        printf( "%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

2、使用qsort排序结构数据

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;
}

//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的
//假设按照名字来⽐较
int cmp_stu_by_name(const void* e1, const void* e2)
{
    return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

//按照年龄来排序
void test2()
{
    struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
    int sz = sizeof(s) / sizeof(s[0]);
    qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}

//按照名字来排序
void test3()
{
    struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
    int sz = sizeof(s) / sizeof(s[0]);
    qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}

int main()
{
    test2();
    test3();
    return 0;
}

重点扩展:

结构成员访问操作符有两种:

  1. .      结构体变量.成员名
  2. ->    结构体指针->成员名
#include <stdio.h>

struct Stu
{
    char name[20];
    int age;
}

//结构成员访问操作符有两种:
//.    结构体变量.成员名
//->    结构体指针->成员名

int main()
{
    struct Stu s = {"zhangsan",20};
    printf("%s %d\n",s.name,s.age);

    struct Stu* ps = &s;
    printf("%s %d\n",(*ps).name,(*ps).age);
    printf("%s %d\n",ps->name,ps->age);

    return 0;
}

        qsort排序默认是升序;若想要实现降序排序的话,则可以将p1和p2的为位置互换一下,这样会使满足的条件相反的交换,形成降序排序

        调用qsort函数这类函数的代码叫做泛型编程,没有指定的类型,只用指针来确定位置,类型可以随意变化使用。

三、qsort函数的模拟实现

使用回调函数,模拟实现qsort(采用冒泡的方式)。
注意: 这里第⼀次使用void* 的指针,讲解 void* 的作用。
#include <stdio.h>
int int_cmp(const void * p1, const void * p2)
{
    return (*( int *)p1 - *(int *) p2);
}

void _swap(void *p1, void * p2, int size)
{
    int i = 0;
    for (i = 0; i< size; i++)
    {
        char tmp = *((char *)p1 + i);
        *(( char *)p1 + i) = *((char *) p2 + i);
        *(( char *)p2 + i) = tmp;
    }
}

void bubble(void *base, int count , int size, int(*cmp )(void *, void *))
{
    //趟数
    int i = 0;
    int j = 0;
    for (i = 0; i< count - 1; i++)
    {
        //一趟内部的两两比较
        for (j = 0; j<count-i-1; j++)
        {
            //比较两个元素
            //if(arr[j]>arr[j+1])
            if (cmp ((char *) base + j*size , (char *)base + (j + 1)*size) > 0)
            {
                //交换两个元素
                _swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
            }
        }
    }
}

int main()
{
    int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
    int i = 0;
    bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
    for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
    {
        printf( "%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

        重点:一定要使用char,使用char的原因:一个一个字节地去交换,char类型大小是一个字节,以一个字节为单位的字节加减可以使精度更准确。


http://www.kler.cn/a/289520.html

相关文章:

  • 一篇文章教会你使用Linux的‘sed‘基础命令
  • Docker了解
  • 构造函数原型对象语法、原型链、原型对象
  • Deepin 系统中安装Rider和Uno Platform
  • Vuex的进阶教程
  • Spring MVC(一)
  • idea问题解决:java: -source 7 中不支持 方法引用 (请使用 -source 8 或更高版本以启用 方法引用)
  • zhidianyun01/基于 ThinkPHP+Mysql 灵活用工+灵活用工源码+灵活用工平台源码
  • EF框架中的修改
  • 利用Spring Boot实现服务降级与熔断机制
  • Spring MVC中的DispatcherServlet:核心调度者
  • 行为型设计模式-备忘录(memento)模式
  • Shell编程基础知识
  • Trm理论 2(Word2Vec)
  • 如何通过k8s来降低运营成本?
  • cuda编程[5]:矩阵乘法--使用静态共享内存优化
  • C/C++语言基础--函数基础(函数定义、调用、生命周期、递归)
  • Linux服务器CPU和IO的监控利器-iostat简介
  • 【重学 MySQL】一、数据库概述
  • 一个好用的Maven依赖冲突解决插件:Maven Helper
  • Flask wtforms组件的作用
  • 【无人机设计与控制】使用 SimMechanics 在 Simulink 中仿真四旋翼飞行器
  • 认识git和git的基本使用,本地仓库,远程仓库和克隆远程仓库
  • 【WPF中的图形(Shape)】
  • 高端控制台使用过程中如何保证用电安全
  • 分类预测|基于蜣螂优化极限梯度提升决策树的数据分类预测Matlab程序DBO-Xgboost 多特征输入单输出 含基础模型