【C语言】指针进阶(二)

还是那句,男孩子在外面要保护好自己~


一、函数指针数组

函数指针也是指针,既然指针数组里是存放指针的,那么也必然可以存放函数的指针

(一)格式

我们知道函数指针,如 int (*p) (int, int),类型为 int (*) (int, int)

还有指针数组,如 int * arr[10] , 类型为 int* [10]

那么 函数指针的格式是什么呢?

int (* arr[10]) (int, int)

arr 先和 [10] 结合,表示arr是一个数组,数组有十个元素,每个元素的类型是int (*) (int, int) 类型为函数的地址即函数指针

所以每个元素都是一个函数指针


二、指向函数指针数组的指针

首先,它是一个指针,再者它是数组的指针,而且数组的每个元素是函数指针,那我们就一步一步来(以下为个例)

指针 (*p)

数组的指针:(*p) [10]

每个元素是一个函数指针:int (* (*p) [10] ) (int, int)


三、回调函数

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

意思是先自定义好了一个函数A,然后把函数名(函数地址)存到指针里,等下在另外一个函数中还要用到函数A,就使用其函数指针来调用函数A,函数A就是一个回调函数。

意思是等下还会用到的那个函数,这个函数就叫回调函数,就有一个要求:用函数指针来调用它

回调,等下还会调用的意思

讲到回调函数就要提到快排 qsort

首先 qsort 函数是一个帮助排序的函数(如把数组排成升序或者说降序)

我们要知道,不仅仅是整型数组可以排序,其他类型如 char类型浮点型数组 也可以排序,还有结构体有时也需要进行排序

一般来说,简单的函数只能排一种类型的数组或只能排结构体(自定义函数形参固定死了)

但是快排 qsort 不同,任意类型的数组或者说结构体它都能排

我这里就有一个问题了,为什么 qsort 函数的形参 能接收 不同类型的数组并将其进行排序(一般来说只能排序一种类型啊)

研究 qsort 函数的参数:

void qsort( viod* base, size_t num, size_t width, int (* cmp) (const void* elem1, const void* elem2) )

首先是 base:base 是目标数组的数组名(数组首元素地址),你要排序的数组名(数组首元素地址)

num :num 表示元素个数

width:width 表示一个元素的大小(所占空间大小,字节为单位)

cmp:从 int (* cmp) (const void* elem1, const void* elem2) 看,首先cmp是一个指针,后面有 () 表示是函数的指针,函数的参数有 elem1、elem2 ,类型为 const void*,是数组的两个元素的地址 ;而该函数返类型为 int。了解到cmp是一个函数指针,函数的地址,等下要调用的(如果记得,会立马反应 cmp 是一个回调函数)

作用:比较数组中的两个元素大小,相减所得的数(可能大于0,小于0,等于0)返回给 qsort ,让qsort给两元素排序

排序基本步骤:

当输入部分完毕后,就要把它进行排序了,接下来那就交给 qsort 函数,但是使用 qsort 函数只要我们做一点:写一个 cmp函数(简单)。

我们只需要写一个简单的 cmp函数,而且大部分是固定格式,底层复杂的排序 qsort 已经给你写好了,就很方便

所以我们只要掌握 cmp函数 怎么写:

刚刚从 qsort 传参里知道了 cmp 函数的参数类型:const void* 和返回类型 int

首先 elem1 的类型:const void* ,说明 elem 是一个 指针,是一串地址。这就是接收两个要进行比较的元素的地址

cmp 的内容:

直接返回量元素相减的值,通过返回的值 大于0 小于0 或等于0 就到qsort内部进行元素相关的排序操作

格式:return 元素 - 元素;

现在只知道 elem 元素的地址,要写成:元素 - 元素

因为 elem 已经是元素的地址了,但是这里一定要注意,elem 的类型为 const void* 是空类型的(才能做到接收容易类型),而 void*类型 的地址不能进行 解引用 或 加一 等操作,所以也必须先强制类型转换,

转换什么类型要看你排序什么类型的数组,这很重要(cmp意义体现)

如排序 int 类型数组,那就 *(int*)elem1 ,这样就把 elem1 地址类型转换了,再通过解引用可找到该元素

现可达成 元素-元素 格式: return *(int*)elem1 - *(int*)elem2;

而排序我们知道,可以排成升序和降序,在cmp函数里是:

升序: return *(int*)elem1 - *(int*)elem2;

降序: return *(int*)elem2 - *(int*)elem1;

记住也可

总的来说,从地址转换到元素:

1、转换elem类型(再解引用)

2、根据升序降序看是 return (e1 - e2) ,还是 (e2 - e1)

函数指针数组和qsort的结合:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>


struct People
{
    char name[20];
    unsigned int age;
};



int cmp_ch(const void* e1, const void* e2)
{
    return *(char*)e1 - *(char*)e2;
}

int cmp_int(const void* e1, const void* e2)
{
    return *(int*)e1 - *(int*)e2;
}

int cmp_float(const void* e1, const void* e2)
{
    double fret = (*(double*)e1 - *(double*)e2);

    return (int)(fret * 100000);
}

int cmp_people_age(const void* e1, const void* e2)
{
    return ((struct People*)e2)->age - ((struct People*)e1)->age;
}

int cmp_people_name(const void* e1, const void* e2)
{
    //return strcmp(((struct People*)e1)->name, ((struct People*)e2)->name);
    return (int)(strlen(((struct People*)e1)->name) - strlen(((struct People*)e2)->name));
}

//排序字符
void test1()
{
    char arr[100] = { 0 };

    printf("请输入一串字符进行排序:");
    scanf("%s", arr);

    qsort(arr, strlen(arr), sizeof(arr[0]), cmp_ch);

    printf("\n%s\n", arr);


}


//排序整型数组
void test2()
{
    int arr[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
    int sz = sizeof(arr) / sizeof(arr[0]);

    qsort(arr, sz, sizeof(arr[0]), cmp_int);

    printf("\n");
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }

}


//排序浮点型数组
void test3()
{
    double arr[10] = { 10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0 };

    int sz = sizeof(arr) / sizeof(arr[0]);

    qsort(arr, sz, sizeof(arr[0]), cmp_float);

    printf("\n");
    for (int i = 0; i < sz; i++)
    {
        printf("%.4lf ", arr[i]);
    }

}


//排序结构体变量
void test4()
{
    struct People s[5] = { {"yangxinrong", 18}, {"lihuangzhu", 19}, {"xukun", 17}, {"yangjun", 15}, {"laojiang", 17} };

    int sz = sizeof(s) / sizeof(s[0]);

    char input[10] = { 0 };

    printf("请输入 “年龄” 或 “名字长度” 来给其排序:");
    scanf("%s", input);

    if (0 == strcmp(input, "年龄"))
    {
        qsort(s, sz, sizeof(s[0]), cmp_people_age);

        for (int i = 0; i < sz; i++)
        {
            printf("\n年龄排名%d :%u\n", i + 1, (s + i)->age);
        }

    }
    else if (0 == strcmp(input, "名字长度"))
    {
        qsort(s, sz, sizeof(s[0]), cmp_people_name);

        for (int i = 0; i < sz; i++)
        {
            printf("\n名字长度排名 %d:%s\n", (i + 1), (s + i)->name);
        }

    }
    else
    {
        printf("非法输入\n");
    }

}


void menu()
{
    printf("          排序字符串        \n");
    printf("         排序整形数组        \n");
    printf("        排序浮点型数组        \n");
    printf("        排序人名或年龄        \n\n");
    printf("输入 “退出程序” 即可退出该程序\n");
    printf("请选择一种类型来进行排序(如输入:“排序字符串”):");
}


int main()
{
    /*test1();*/
    /*test2();*/



    do
    {
        void (*p[5])() = { NULL, test1, test2, test3, test4 };

        menu();
        char input[20] = { 0 };
        scanf("%s", input);

        if (0 == strcmp(input, "排序字符串"))
        {
            (*(p + 1))();
        }
        else if (0 == strcmp(input, "排序整型数组"))
        {
            (*(p + 2))();
        }
        else if (0 == strcmp(input, "排序浮点型数组"))
        {
            (*(p + 3))();
        }
        else if (0 == strcmp(input, "排序人名或年龄"))
        {
            (*(p + 4))();
        }
        else if (0 == strcmp(input, "退出程序"))
        {
            break;
        }
        else
        {
            system("cls");
            printf("非法输入\n");
            Sleep(2000);
        }

        Sleep(3000);

        system("cls");

    } while (1);

    return 0;
}

相关试题就放到后面再更新咯,大家记得电赞哦,不然晚上会梦见小只因哦(真的,昨天晚上我就梦见了)

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Meiyourou.c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值