C语言学习记录——삼십 指针详解(3)

目录

一、回调函数

二、qsort


一、回调函数

上一篇写了一点回调函数,这一篇继续写。

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

一个简单的例子

void print(const char* str)
{
    printf("hehe: %s", str);
}

void test(void (*p)(const char*))
{
    printf("test\n");
    p("codes");
}

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

print就是一个回调函数。这种机制叫回调函数的机制。接下来要写一个比较符合回调机制的例子,在这之前,先写一个复杂的概念。

二、qsort

之前学过的冒泡排序,只能排序整形数组。不过库函数qsort都可排序。先写一个冒泡排序的程序。

void bubble_sort(int arr[], int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        int j = 0;
        for (j = 0; j < sz - 1; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}

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

如果要排序一个结构体数组,浮点型数组,这个函数就没法排序。算法不通用。看看qsort这个函数原版:

void qsort(void* base,
                 size_t num,
                 size_t width,
                 int (*compare)(const void* elem1, const void* elem2));

这个函数总共有4个参数。最后一个能看出来是函数指针。第一个base,是数组名起始位置,第二个num是数组的大小,第三个width是单个元素的字节 ,第四个是一个比较函数,要传过去比较对象的地址。对于这个比较函数,思路还是一样,需要对应着比较,但是比较方法各不一样,比如对于两个名字,排序就不能单纯比大小。既然有不同的方法,那么定义的函数就不能相同。

const void* elem1, const void* elem2。函数要带的参数,类型是void*,无类型指针。这是一个可以接受任何类型变量地址的类型。qsort可以接受任何一种类型的排序,所以需要用到void*。

不过如果定义一个变量,把这个变量的地址放到void*类型的指针里去,然后解引用更改值,会发生非法间接寻址。因为void*是无类型,所以访问时具体访问几个字节无法得知,所以void*类型的指针无法进行解引用操作。同样,++和--也不可。

compare这个函数,进入函数体后比较时,因为是void*类型,所以比较也无法进行,需要进行强制类型转换。这个函数还有一个要求,如果第一个小于第二个,那么返回一个负数,相等返回0,大于返回一个正数。那么写一个浮点数和整型的排序

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

int cmp_float(const void* e1, const void* e2)
{
    return ((int)(*(float*)e1 == *(float*)e2));
}

void test1()
{
    int arr[10] = { 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);
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

void test2()
{
    float f[] = { 9.0, 8.0, 7.0, 6.0, 5.0 };
    int sz = sizeof(f) / sizeof(f[0]);
    qsort(f, sz, sizeof(f[0]), cmp_float);
    int j = 0;
    for (j = 0; j < sz; j++)
    {
        printf("%f ", f[j]);
    }
    printf("\n");
}

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

也可以用排序结构体

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

void test3()
{
    struct Stu s[3] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 10} };
    int sz = sizeof(s) / sizeof(s[0]);
    qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}

这是按照年龄比,结果是王五排在前面。也可以按照名字排序。但是名字比较,也就是字符串比较了,不能直接用大小比较,应该用strcmp函数,比较。

也是相等返回0。排好之后就是lisi,wangwu,zhangsan排序。

回顾一下,比较函数的四个参数

1、待排序数组的首元素地址

2、 待排序数组的元素个数

3、 待排序数组的每个元素大小—单位是字节

4、是函数指针,比较两个元素的所用函数的地址—这个函数使用者自己实现函数指针的两个参数是:待比较的两个元素的地址

写一个函数实现qsort功能。还是定义bubble_sort函数,test4里面放整形数组,test5里面放结构体数组,用同一个定义函数。

#include <stdlib.h>

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

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)
{
    return strcmp(((struct Stu*)e1)->name, ((strutc Stu*)e2)->name);
}

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(void* base, int sz, int width, int(*cmp)(void* e1, void* e2))
{
    int i = 0;
    for (i = 0; i < sz - 1; i++)
    {
        int j = 0;
        for (j = 0; j < sz - 1; 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 test4()
{
    int arr[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
}

void test5()
{
    struct Stu s[3] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 10} };
    int sz = sizeof(s) / sizeof(s[0]);
    bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}

int main()
{
    test4();
    test5();
    return 0;
}

要去模仿这个qsort函数,除了功能外,参数类型也可以参考。现在定义bubble_sort这个函数来实现相关功能,比如4和5,一个整形,一个结构体。sort函数的参数类型我们可以按照qsort函数来写,起始位置void* base,元素个数int sz,然后我们要去访问传过来的参数,只知道了首元素地址,元素个数,接下来要进行循环,一个个元素比较进行排列的话,我们需要知道元素类型,以此来决定一次前进几个字节,但是传类型这种操作暂且不知道,参考一下qsort,我们也可以写上int width,也就是用来传单个元素大小,这样也就是间接知道了元素。先不看第四个参数,进入函数体,要进行排序,那么两个for循环是必定的,声明 i 作为要循环的趟数,然后 j  的for循环里要比较两个元素。这是个难点,要定义的bubble_sort函数时模仿qsort函数,通用型排序,如何做比较是重点。这里需要两步,先判断这两个传进来的元素先后,大小等,然后再进项交换。判断这部分,首先需要传参,那么回到void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2)),我们需要括号里第四个参数,一个比较函数。接受函数的地址,用指针变量,对于传进来的参数,不仅是整形,浮点型等各种类型都有,所以定义void*类型的指针变量来接收它们的地址。传进来后,运行到主要的判断,交换的函数体部分,判断和交换我们用两个函数来作用。我们需要读取到这两个参数,并且进行循环,持续判断所有的元素,所以不能继续用void*来接收。但是int*可以吗?也不行,判断完一次就越过4个字节,如果是double类型的参数,则不适用。那么看char*类型,是一个字节一个字节的前进。(char*)base,表示首元素地址,我们把他强制类型转换,和它要比较的第二个元素如何表达?如果是一个8个字节的元素,那么需要+8,那么不同类型的参数要加多少,都不一样。这时候看看之前的width,之前用了width作为每个元素的大小,如果使用width,那么就可以实现通用,传进来不同的函数就前进相应的字节数。那么我们这样写:

(char*)base + j * width, (*char)base + (j + 1) * width。j  为0时,就是(char*)base和(char*)base + 1*width,这两个元素进行比较,这样就能完美地进行下去了。这时候再写比较函数。这个函数我们需要不同类型写不同函数,比如cmp_int,cmp_stu_by_age,cmp_stu_by_name。没法写出一个通用的函数,结构体和整形的比较方法不一样,所以我们需要写多个函数,对应不同的元素类型,所以在test4和test5里面调用bubble_sort这个函数时,第四个参数就写对应的比较函数,然后bubble这个函数就会接收,进行比较。这里在进行结构体排序时,按照年龄是可以简单的排列,按照姓名就是字符串的排列,字符串排列需要用到strcmp这个库函数,头文件是stdlib.h,传入的参数之前写过,是char*类型,所以就用char*的指针变量来接收。 姓名的排序,首字母z最大,a最小。strcmp的排序也会和数字排序一样,返回相应的值,0 ,>0,<0的数。这一部分完成后,再就是交换,交换写一个Swap函数,Swap括号里也是char*类型的指针变量接收参数。进入函数体,我们要进行循环,我们要循环多少次?所以我们还需要用到width。让i < width,这样和元素类型相符合的循环次数也就定下来了。之后再在main函数里面调用test函数,整个程序也就结束了。这样如果还需要排序别的类型的话,只需要写一个这个类型的比较函数,在调用bubble_sort时写上这个函数,就可以了。

结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值