c语言从入门到实战——回调函数与qsort的讲解和模拟实现

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
可观测链路 OpenTelemetry 版,每月50GB免费额度
可观测监控 Prometheus 版,每月50GB免费额度
简介: 回调函数是一个函数,它作为参数传递给另一个函数,并且能够在该函数内部被调用。在C语言中,回调函数通常被用于实现事件处理和排序算法中。`qsort`是C标准库中的一个排序函数,它可以对任意类型的数组进行排序。`qsort`需要三个参数:要排序的数组、数组元素的个数和一个指向回调函数的指针。回调函数必须满足两个条件:能够比较数组中的元素,返回一个整数表示它们之间的大小关系;并且它应该能够被`qsort`函数调用。回调函数是一种在编程中广泛使用的技术,它允许一个函数作为参数传递给另一个函数,并在需要时被调用。这种机制使得代码更加灵活和可重用。

回调函数与qsort的讲解和模拟实现


前言

回调函数是一个函数,它作为参数传递给另一个函数,并且能够在该函数内部被调用。在C语言中,回调函数通常被用于实现事件处理和排序算法中。

qsort是C标准库中的一个排序函数,它可以对任意类型的数组进行排序。qsort需要三个参数:要排序的数组、数组元素的个数和一个指向回调函数的指针。回调函数必须满足两个条件:能够比较数组中的元素,返回一个整数表示它们之间的大小关系;并且它应该能够被qsort函数调用。

回调函数是一种在编程中广泛使用的技术,它允许一个函数作为参数传递给另一个函数,并在需要时被调用。这种机制使得代码更加灵活和可重用。

qsort是C语言标准库中的一个函数,用于对数组进行快速排序。它使用了回调函数作为比较函数,允许用户自定义排序规则。这使得qsort可以处理各种类型的数据,并根据不同的排序需求进行调整。

模拟实现qsort可以通过创建一个简单的排序函数来完成,该函数接受一个数组、数组的大小、比较函数作为参数。在排序过程中,使用比较函数来确定元素的顺序,并根据需要交换元素的位置。通过模拟实现qsort,可以更好地理解回调函数在排序算法中的应用,以及如何使用自定义的比较函数来满足不同的排序需求。

总之,回调函数在编程中是一种强大的技术,它使得代码更加灵活和可重用。qsort是一个使用回调函数的示例,它允许用户自定义排序规则,从而适应不同的排序需求。通过模拟实现qsort,可以深入了解回调函数在排序算法中的应用。


1. 回调函数是什么?

C语言中,回调函数是指将一个函数作为参数传递给另一个函数,并在后者中被调用的函数。

一般情况下,回调函数被用来在程序中实现事件处理和消息传递等机制。例如,当一个用户在应用程序中点击一个按钮时,应用程序会调用相应的回调函数来处理该事件。

以下是一个示例代码,展示了如何在C语言中定义和使用回调函数:

#include <stdio.h>
// 回调函数定义
typedef int (*callback)(int);
// 回调函数实现
int callback_function(int num) {
    return num * 2;
}
// 接收回调函数参数的函数
void accept_callback(int num, callback cb) {
    int result = cb(num); // 调用回调函数
    printf("The result is: %d\n", result);
}
int main() {
    // 调用 accept_callback 函数,并传入回调函数指针
    accept_callback(5, callback_function);
    return 0;
}

在上述示例中,我们通过定义 callback 类型为函数指针类型,从而定义了一个回调函数类型。接着,我们定义了回调函数 callback_function,该函数接收一个整数作为参数,并返回该参数的两倍。最后,我们通过调用 accept_callback 函数,并传入一个整数以及回调函数的指针,实现了回调函数的调用和结果输出。

需要注意的是,回调函数的实现和使用需要满足一定的约定,例如回调函数的参数和返回值类型需要与被调用函数的要求一致,否则会导致程序运行错误。

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

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

//使用回调函数改造前
#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", r
  break;
  case 2:
  printf("输入操作数:");
  scanf("%d %d", &x, &y)
  ret = sub(x, y);
  printf("ret = %d\n", r
  break;
  case 3:
  printf("输入操作数:");
  scanf("%d %d", &x, &y)
  ret = mul(x, y);
  printf("ret = %d\n", r
  break;
  case 4:
  printf("输入操作数:");
  scanf("%d %d", &x, &y)
  ret = div(x, y);
  printf("ret = %d\n", r
  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;
  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;
}
#include <stdio.h>
#include <stdlib.h>
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 dive(int x, int y)
{
  return x / y;
}
int main()
{
  int a,x,y;
  int (*p[4])(int x, int y) = { add,sub,mul,dive };
  while (1)
  {
    printf("需要计算的数字\n");
    scanf_s("%d%d", &x, &y);
    printf("需要进行的操作  1 . + 2 . - 3. *  4. /  0. 退出 \n");
    scanf_s("%d", &a);
    switch (a)
    {
    case 1:printf("%d", p[0](x, y)); break;
    case 2:printf("%d", p[1](x, y)); break;
    case 3:printf("%d", p[2](x, y)); break;
    case 4:printf("%d", p[3](x, y)); break;
    case 0:exit(0); break;
    default:printf("error"); continue;
    }
  }
  system("pause");
  return 0;
}

2. qsort

qsort是C语言中的一个标准库函数,用于实现快速排序算法。它可以对任意类型的数组进行排序,只需要给出相应的比较函数即可。

qsort的函数原型如下:

void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));

其中,base是要排序的数组的首地址,nmemb是数组中元素的个数,size是每个元素的大小(以字节为单位),compar是用来比较数组中元素大小的函数指针。

比较函数的定义如下:

int compar(const void *a, const void *b);

函数需要返回一个整型值,表示两个元素的大小关系。如果a小于b,返回一个负数;如果a等于b,返回0;如果a大于b,返回一个正数。

下面是一个使用qsort进行int类型数组排序的例子:

#include <stdio.h>
#include <stdlib.h>
int cmp(const void *a, const void *b) {
    return *(int *)a - *(int *)b;
}
int main() {
    int arr[] = {3, 1, 4, 1, 5, 9, 2, 6};
    int n = sizeof(arr) / sizeof(int);
    qsort(arr, n, sizeof(int), cmp);
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}
运行结果为:1 1 2 3 4 5 6 9

上述代码中,我们定义了一个比较函数cmp,返回a-b的结果,然后将其传给qsort函数进行排序。在main函数中,我们定义了一个int类型的数组arr,调用qsort进行排序后,输出结果即可。

需要注意的是,qsort函数是一个不稳定的排序算法,即排序后可能改变数组中相同元素的原有顺序。

2.1 使用qsort函数排序整型数据

#include <stdio.h>
//qosrt函数的使用者得实现一个比较函数
int int_cmp(const void * p1, const void * p2)
{
  return (*( int *)p1 - *(int *) p2);
}
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.2 使用qsort排序结构数据

#include <stdio.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;
}
//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;
}

汇总

#define _CRT_SECURE_NO_WARNINGS 1 
#include <stdio.h>
#include <string.h>
typedef struct stu
{
    char name[10];
    char num[12];
    int age;
}stu;
int int_cmp(const void* a,const void * b)
{
    return *(int*)a - *(int*)b;
}
int double_cmp(const void* a, const void* b)
{
    return *(double*)a - *(double*)b;
}
int char_cmp(const void* a, const void* b)
{
    return strcmp((char*)a, (char*)b);
}
int stu_name_cmp(const void* a, const void* b)
{
    return strcmp(((stu*)a)->name, ((stu*)b)->name);
}
int main()
{
    stu s1[5] = {{"zhangsan", "123156652", 12}, {"lisi","123491235652",12},
    {"wangwu", "19642588652", 12},
    { "jia","1321458652",12 },
    { "yi","19635288652",12 }};
    stu* p;
    int arr[] = { 3, 1, 4, 1, 5, 9, 2, 6 };
    double arr1[] = { 3.5, 1.3, 4.2, 1.6, 5.5, 9.4, 2.3, 6.6 };
    char arr3[] = "abzcdefgd ";
    int len = (int)strlen(arr3);
    int n = sizeof(arr) / sizeof(int);
    int n1 = sizeof(stu) / sizeof(s1[0]);
    p = s1;
    qsort(arr, n, sizeof(int), int_cmp);
    qsort(arr1, n, sizeof(double), double_cmp);
    qsort(arr3, len, sizeof(char), char_cmp);
    qsort(s1, n1, sizeof(stu), stu_name_cmp);
    for (int i = 0; i < n; i++) 
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
    for (int i = 0; i < n; i++)
    {
        printf("%lf  ", arr1[i]);
    }
    printf("\n");
    printf("%s", arr3);
    printf("\n");
    for (int i = 0; i < n; i++)
    {
        printf("%s ", (s1[i]).name);
    }
    return 0;
}

3. qsort函数的模拟实现

使用回调函数,模拟实现qsort(采用冒泡的方式)。

#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 (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;
}
#define _CRT_SECURE_NO_WARNINGS 1 
#include <stdio.h>
#include <string.h>
typedef struct stu
{
    char name[10];
    char num[12];
    int age;
}stu;
int int_cmp(const void* a,const void * b)
{
    return *(int*)a - *(int*)b;
}
int double_cmp(const void* a, const void* b)
{
    return *(double*)a - *(double*)b;
}
int char_cmp(const void* a, const void* b)
{
    return strcmp((char*)a, (char*)b);
}
int stu_name_cmp(const void* a, const void* b)
{
    return strcmp(((stu*)a)->name, ((stu*)b)->name);
}
void _swap(void* p1, void* p2, int size)
{
    int i = 0;
    char temp;
    for (i = 0; i < size; i++)
    {
        temp = *((char*)p1 + i);
        *((char*)p1 + i) = *((char*)p2 + i);
        *((char*)p2 + i) = temp;
    }
}
void my_qsort(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 (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
            {
                _swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
            }
        }
    }
}
int main()
{
    stu s1[5] = {{"zhangsan", "123156652", 12}, {"lisi","123491235652",12},
    {"wangwu", "19642588652", 12},
    { "jia","1321458652",12 },
    { "yi","19635288652",12 }};
    stu* p;
    int arr[] = { 3, 1, 4, 1, 5, 9, 2, 6 };
    double arr1[] = { 3.5, 1.3, 4.2, 1.6, 5.5, 9.4, 2.3, 6.6 };
    char arr3[] = "abzcdefgd ";
    int len = (int)strlen(arr3);
    int n = sizeof(arr) / sizeof(int);
    int n1 = sizeof(stu) / sizeof(s1[0]);
    p = s1;
    my_qsort(arr, n, sizeof(int), int_cmp);
    qsort(arr1, n, sizeof(double), double_cmp);
    qsort(arr3, len, sizeof(char), char_cmp);
    qsort(s1, n1, sizeof(stu), stu_name_cmp);
    for (int i = 0; i < n; i++) 
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
    for (int i = 0; i < n; i++)
    {
        printf("%lf  ", arr1[i]);
    }
    printf("\n");
    printf("%s", arr3);
    printf("\n");
    for (int i = 0; i < n; i++)
    {
        printf("%s ", (s1[i]).name);
    }
    return 0;
}


相关文章
|
29天前
|
安全 编译器 C语言
C++入门1——从C语言到C++的过渡
C++入门1——从C语言到C++的过渡
45 2
|
29天前
|
存储 Java 编译器
初识C语言1——C语言入门介绍
初识C语言1——C语言入门介绍
27 1
|
1月前
|
算法 搜索推荐 C语言
【C语言篇】深入理解指针4(模拟实现qsort函数)
【C语言篇】深入理解指针4(模拟实现qsort函数)
21 2
|
9天前
|
C语言
【c语言】qsort函数及泛型冒泡排序的模拟实现
本文介绍了C语言中的`qsort`函数及其背后的回调函数概念。`qsort`函数用于对任意类型的数据进行排序,其核心在于通过函数指针调用用户自定义的比较函数。文章还详细讲解了如何实现一个泛型冒泡排序,包括比较函数、交换函数和排序函数的编写,并展示了完整的代码示例。最后,通过实际运行验证了排序的正确性,展示了泛型编程的优势。
11 0
|
24天前
|
C语言
回溯入门题,数据所有排列方式(c语言)
回溯入门题,数据所有排列方式(c语言)
|
2月前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
317 8
|
30天前
|
搜索推荐 C语言
深入浅出理解 C 语言中的 qsort 函数
深入浅出理解 C 语言中的 qsort 函数
|
2月前
|
存储 算法 C语言
C语言手撕实战代码_二叉排序树(二叉搜索树)_构建_删除_插入操作详解
这份二叉排序树习题集涵盖了二叉搜索树(BST)的基本操作,包括构建、查找、删除等核心功能。通过多个具体示例,如构建BST、查找节点所在层数、删除特定节点及查找小于某个关键字的所有节点等,帮助读者深入理解二叉排序树的工作原理与应用技巧。此外,还介绍了如何将一棵二叉树分解为两棵满足特定条件的BST,以及删除所有关键字小于指定值的节点等高级操作。每个题目均配有详细解释与代码实现,便于学习与实践。
|
2月前
|
存储 算法 C语言
C语言手撕实战代码_二叉树_构造二叉树_层序遍历二叉树_二叉树深度的超详细代码实现
这段代码和文本介绍了一系列二叉树相关的问题及其解决方案。其中包括根据前序和中序序列构建二叉树、通过层次遍历序列和中序序列创建二叉树、计算二叉树节点数量、叶子节点数量、度为1的节点数量、二叉树高度、特定节点子树深度、判断两棵树是否相似、将叶子节点链接成双向链表、计算算术表达式的值、判断是否为完全二叉树以及求二叉树的最大宽度等。每道题目均提供了详细的算法思路及相应的C/C++代码实现,帮助读者理解和掌握二叉树的基本操作与应用。
|
2月前
|
存储 算法 C语言
C语言手撕实战代码_循环单链表和循环双链表
本文档详细介绍了用C语言实现循环单链表和循环双链表的相关算法。包括循环单链表的建立、逆转、左移、拆分及合并等操作;以及双链表的建立、遍历、排序和循环双链表的重组。通过具体示例和代码片段,展示了每种算法的实现思路与步骤,帮助读者深入理解并掌握这些数据结构的基本操作方法。