指针(进阶)(下)

指针(进阶)(下),第1张

目录: 1、回调函数         1.1、回调函数的概念         1.2、回调函数的使用                      1.2.1、案例一                      1.2.2、案例二          1.3、qsort函数                         1.3.1、qsort函数介绍                         1.3.2、qsort函数的使用                         1.3.3、用冒泡排序思想模拟实现qsort         
1、回调函数 1.1、回调函数的概念

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

 理解:如果拿到一个a函数的地址,通过a函数的地址,再去调用它所指向的函数的时候,被调的那个函数(a函数)就被叫做回调函数。

 1.2、回调函数的使用    1.2.1、   案例一:

        ​​​​​计算器的模拟实现:

#include
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 menu()
{
    printf("***********************\n");
    printf("**** 1.add  2.sub  ****\n");
    printf("**** 3.mul  4.div  ****\n");
    printf("**** 0.exit        ****\n");
    printf("***********************\n");
}

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("请输入两个 *** 作数:>");//每个case语句中都要写这两句代码这样就使代码冗 
                                        //余
            scanf("%d %d", &x,&y);
            ret = add(x,y);
            printf("%d\n",ret);
            break;
        case 2:
            printf("请输入两个 *** 作数:>");
            scanf("%d %d", &x,&y);
            ret = sub(x,y);
            printf("%d\n",ret);
            break;
        case 3:
            printf("请输入两个 *** 作数:>");
            scanf("%d %d", &x,&y);
            ret = mul(x,y);
            printf("%d\n",ret);
            break;
        case 4:
            printf("请输入两个 *** 作数:>");
            scanf("%d %d", &x,&y);
            ret = div(x,y);
            printf("%d\n",ret);
            break;
        case 0:
            printf("退出计算器\n");
            break:
        defalut:
            printf("选择错误\n");
            break;
        }
    } while (input);
}

那么改进这个代码,就现在掌握的知识有两种方法 

1、使用函数指针数组来实现

2、使用回调函数

这里使用回调函数来进行改进

#include
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 menu()
{
    printf("***********************\n");
    printf("**** 1.add  2.sub  ****\n");
    printf("**** 3.mul  4.div  ****\n");
    printf("**** 0.exit        ****\n");
    printf("***********************\n");
}
//这里通过重新封装一个函数,来解决代码冗余的问题。
void calc(int (*p)(int,int))//将add、mul、sub、div函数的地址传给calc函数,calc函数使用 
 //指针p来接收函数地址。
{
    int x = 0;
    int y = 0;
    int ret = 0; 
    scanf("%d %d",&x,&y);
    ret = p(x,y);//在适当的位置通过函数指针调用加、减、乘、除函数
    printf("%d\n",ret);
}

int main()
{
    int input = 0;
    int x = 0;
    int y = 0;
    int ret = 0; 
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        
        switch (input)
        {
        case 1:
                calc(add);//这里将add函数(函数名相当于函数地址,这里是传址调用)作为参 
                          //数传给calc函数
            break;
        case 2:
                calc(sub);
            break;
        case 3:
                calc(mul);
            break;
        case 4:
                calc(div);
            break;
        case 0:
            printf("退出计算器\n");
            break:
        defalut:
            printf("选择错误\n");
            break;
        }
    } while (input);
}

这样的机制就叫做回调函数的机制
 1.2.2、案例二:

先来回忆一下前面说过的冒泡排序,分析一下冒泡排序中存在的问题。

冒泡排序

#include
    //bubble_sort函数只能排序整型数据
void bubble_sort(int arr[],int sz)//写死之后,参数就固定了,
{
    //趟数
    int i = 0;
    for(i = 0;i < sz - 1;i++)
    {
        //一趟冒泡排序的过程
        int j = 0;
        for(j = 0;j arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = tmp;
            }
        }
    }
}
void print(int arr[],int sz)
{
    int i = 0;
    for(i = 0;i 

上述代码的问题是,一但函数写死之后,参数就固定了,就只能排序固定的数据了。

 分析如何用qsort函数来解决冒泡排序中存在的问题

1.3、qsort函数

首先来了解一下qsort函数

qsort函数是C语言标准库提供的排序函数,qsort函数的底层是快速排序的思想。 

1.3.1、qsort函数介绍

 qsort函数可以排序任意类型的数据

comper是一个指针,指向一个比较函数,这个函数的参数是const void*,const void*

comper的意思为比较

(1)、对qsort函数的分析

        base指针指向待排序数组的第一个元素
        num表示这个数组有几个待排序的元素
        size表示这些待排序的数据的大小,单位(字节)
        compar表示指针指向一个函数,这个函数用来比较两个元素

(2)、对参数void* base的解读

        1>、void*的指针表示为无具体类型的指针,所以void*的指针可以接收任意类型的地址

                由于qsort函数可以排序任意类型的数据,所以用void*作为指针base的指针型。

         2>、但是 void *p = &i;时,不能直接进行解引用 *** 作,

                  可以将指针p强制类型转换成与i相应的指针类型来解引用。

例如:


            

 (3)、对参数int(*comper)(const void*,const void*)进行解读

         1>、 由于qsort函数可以对任意类型的数据进行排序,

                  例如:整型、浮点型、字符串、结构体等。

                   由于每个类型的比较方式不同,所以比较函数由用户自己编写,将函数地址传给                          qsort函数,所以用指针comper来接收比较函数的地址

          2>、返回类型为int

                qsort函数规定:

                 

 当第一个指针指向的元素<第二个指针指向的元素,返回一个小于0的数子;

 当第一个指针指向的元素=第二个指针指向的元素,返回一个等于0的数子;

 当第一个指针指向的元素>第二个指针指向的元素,返回一个大于0的数子;             

1.3.2、 qsort函数的使用

 1、整型类型比较

#include
#include
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)
{
    if (*(int*)e1 < *(int*)e2)
        return -1;
    else if(*(int*)e1 > *(int*)e2)
        return 1;
    else
        return 0;
}

//int cmp_int(const void* e1, const void* e2)
//{
//    return(*(int*)e1 - *(int*)e2);
//}可以用这个代码优化前面的比较函数,可以更好的控制排序为降序或升序

int main()
{
    int arr[] = { 2,1,3,7,5,9,6,8,0,4 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, sz, sizeof(arr[0]), cmp_int);
    print(arr, sz);
    return 0;
}

2、结构体排序

1>、按名字排序

#include
#include
#include
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 print(struct Stu* s, int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%s %d\n", s[i].name, s[i].age);
    }
}

int main()
{
    struct Stu s[] = { {"zhangsan", 20},{"lisi", 55},{"wangwu", 40} };
    //按名字比较
    int sz = sizeof(s) / sizeof(s[0]);
    qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
    print(s,sz);
    return 0;
}

这是按照字符串中字母对应的ASCII码值进行比较

 2>、按年龄比较

#include
#include
#include
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 print(struct Stu* s, int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%s %d\n", s[i].name, s[i].age);
    }
}

int main()
{
    struct Stu s[] = { {"zhangsan", 20},{"lisi", 55},{"wangwu", 40} };
    //按名字比较
    int sz = sizeof(s) / sizeof(s[0]);
    qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
    print(s,sz);
    return 0;
}

结果为:

1.3.3、用冒泡排序思想模拟实现qsort
#include
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, int width)
{
    int i = 0;
    for (i = 0; i < width; i++)//这里用整型数据进行说明,在内存中每个元素占4个字节, 
 //width=4,一个字节一个字节进行交换,
    {
        char tmp = *buf1;//这里申请char类型的数据空间,原因是char类型的数据占一个内存空 
 //间,指针buf指向的内容也占一个字节的内存空间
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}

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 j = 0;
        for (j = 0; j < sz - 1 - i; j++)
        {
            if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
            //通过指针cmp来调用函数cmp_int,将两个待比较数据的地址传给cmp_int函数
            {
                Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
                //Swap函数用来进行比较后的交换,这里交换是一个字节一个字节来交换数据。 
 //base的类型是void*,强制类型转换成char*,width是数据的宽度, 

            }
        }
    }
}
int cmp_int(const void* e1, const void* e2)
{
    return(*(int*)e1 - *(int*)e2);//这里可以改变数组的排序方式
}

void test()
{
    int arr[] = { 2,1,3,7,5,9,6,8,0,4 };
    int sz = sizeof(arr) / sizeof(arr[10]);
    bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
    print(arr, sz);
}

int main()
{
    test();
    return 0;
}

上述代码画图将讲解:

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/3001831.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-27
下一篇 2022-09-27

发表评论

登录后才能评论

评论列表(0条)