【C语言】C语言数组和指针-2

简介: 【C语言】C语言数组和指针-2

四、回调函数

1.回调函数的概念(用函数指针调用的函数)


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


我们直接大白话给他翻译成通俗易懂的语言。怎么样就是回调函数呢?就比如你现在有一个可以实现两数之和功能的函数Add,你明明可以在main函数里面直接调用这个函数,给他传上两个整数的参数,让他返回和的值。但是,什么叫回调函数呢?其实就是你稍微拐了个弯儿,你把这个函数作为参数传递给一个Calc函数,然后Clac函数的参数被设计成为一个指向Add函数的函数指针,然后我们在Calc函数中,用接收Add函数的函数指针p(假设指针的名字是p)重新调用Add函数,这时Add函数就被称为回调函数


2.回调函数的使用场景

2.1使用场景一:


我们先用上面那个代码,来应用一下回调函数的使用

如果我们想要实现加减乘除这些函数功能的实现,除了上方写一个函数指针数组来实现,也还可以用switch的语句来实现,例如下面的代码:

void menu()
{
  printf("####################\n");
  printf("### 1.add  2.sub#####\n");
  printf("### 3.mul  4.div#####\n");
  printf("##### 0.exit ########\n");
  printf("#####################\n");
}
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;
}
int main()
{ 
  int input = 0;
  int x = 0;
  int y = 0;
  do
  {
    menu();
    printf("请选择:>\n");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      printf("请输入两个操作数:>\n");
      scanf("%d %d", &x, &y); 
      printf("%d\n", add(x, y));   
      break;
    case 2:
      printf("请输入两个操作数:>\n");
      scanf("%d %d", &x, &y);
      printf("%d\n", sub(x, y));
      break;
    case 3:
      printf("请输入两个操作数:>\n");
      scanf("%d %d", &x, &y);
      printf("%d\n", mul(x, y));
      break;
    case 4:
      printf("请输入两个操作数:>\n");
      scanf("%d %d", &x, &y);
      printf("%d\n", div(x, y));
      break;
    case 0: 
      printf("退出程序\n");
      break;
    default:
      printf("选择错误,重新选择\n");
      break;
    }
  } while (input);
  return 0;
}

如果我们需要一个函数Calc实现4种函数功能的话,那这个函数参数就必须是函数指针,我们在Calc函数里实现了四种运算法则的函数,这四种函数就是回调函数


2.2使用场景二:

void print( const char* str)
{
  printf("%s\n", str);
}
void test(void(*p)( const char*))
{
  p("I LOVE YOU");//将字符串首字符地址传过去了
}
int main()
{
  test(print);
  return 0;
}



2.3使用场景三qsort函数:

我们先介绍一下,qsort函数如何使用吧😁

f146e47c503b480aac837b94152dc5d2.png

043c7306132b475c9486a4f637145cbd.png

1f3a33973f4241c19d3546f1871cd5df.png

parameters是参数的意思,第一张图片向我们介绍了,qsort函数的返回类型和参数类型,第二张图片向我们介绍了各种参数所代表的意思

base是你要排序的数组的起始地址,num是数组所有元素的大小(而不是所有元素的字节的大小),width是数组每个元素的字节大小,最后一个参数是一个函数指针,用来接收你传过去的函数名(也就是函数地址)第三张图片是我们所设计的函数的功能,他只要负责返回大于0或小于0或等于0的数字就完全OK了

下面通过代码,来演示一下qsort函数的使用


int cmp(const void* p1, const void* p2)
{
  return (*(int*)p1 - *(int*)p2);
}
int main()
{
  int arr[10] = { 1,3,5,7,9,2,4,6,8,0 };
  int i = 0;
  qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), cmp);
  for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}


a39e1dafa37042ea91e573ee3a0995ac (1).png



我们完美的实现了,qsort函数的使用**(如果你阅读到了这里,小编真心佩服你,你一定能拿到理想的offer的,我们一起加油,耶耶耶🤩🤩🤩)**


2.4自己实现一下qsort函数

struct stu
{
  char name[20];
  int age;
  char sex[5];
  int tele[12];
};
int cmp_by_int(void* buf1, void* buf2)
{
  return (*(int*)buf1 - *(int*)buf2);//需要把无类型指针转换为int*的指针,因为我们需要返回一个整数(>0,<0,=0这三种)
}
int cmp_by_float(void* buf1, void* buf2)
{
  return (*(int*)buf1 - *(int*)buf2);
}
int cmp_by_struct_by_age(void* buf1, void* buf2)
{
  return ((struct stu*)buf1)->age - ((struct stu*)buf2)->age;
}
void swap(char* e1, char* e2,int width)
{
  int i = 0;
  for (i = 0; i < width; i++)
  {
    char tmp = '1';
    tmp = *e1;
    *e1 = *e2;
    *e2 = tmp;
    e1++;
    e2++;
  }
}
void my_qsort(void* base, int num, int width, int(*ps)(void* elem1, void* elem2))
{
  //确定冒泡排序的趟数
  int i = 0;
  for (i = 0; i < num; i++)
  {
    int j = 0;
    //确定每一趟需要排序的元素对的对数
    for (j = 0; j < num - 1 - i; j++)
    {
      //确定是否要进行交换元素,也就是是否要排序
      if (ps((char*)base + j*width, (char*)base + (j+1)*width) > 0)
      //不同元素类型的字节宽度是不一样的,所以传j*width,不同元素传过去的地址大小是不同的
      {
        //进行交换元素
        swap((char*)base + j*width, (char*)base + (j+1)*width, width);
        //我们在进行交换元素时,其实道理和上面的if判断条件是相同的,我们传过去的地址大小也是无法确定的
        //所以要传他字节宽度的整数倍
      }
      else
      {
        ;
      }
    }
  }
}
void test3()
{
  struct stu s1[] = { {"zhangsan",20,"man",15598303778},
    { "wangwu",30,"women",13232746588 },
    { "lisi", 40, "man", 13231244563 } , };
  my_qsort(s1, sizeof(s1) / sizeof(s1[0]),sizeof(s1[0]), cmp_by_struct_by_age);
}
void test2()
{
  float arr2[10] = { 1.0,3.0,5.0,7.0,9.0,2.0,4.0,6.0,8.0,10.0 };
  my_qsort(arr2, sizeof(arr2) / sizeof(arr2[0]), sizeof(arr2[0]), cmp_by_float);
  int i = 0;
  for (i = 0; i < sizeof(arr2) / sizeof(arr2[0]); i++)
  {
    printf("%f ", arr2[i]);
  }
}
void test1()
{
  int arr1[10] = { 1,3,5,7,9,2,4,6,8,0 };
  my_qsort(arr1, sizeof(arr1) / sizeof(arr1[0]), sizeof(arr1[0]), cmp_by_int);
  int i = 0;
  for (i = 0; i < sizeof(arr1) / sizeof(arr1[0]); i++)
  {
    printf("%d ", arr1[i]);
  }
  printf("\n");
}
int main()
{
  test1();
  test2();
  test3();
  return 0;
}


bf17e819072d4dfa8ad34fc0e0195251.png


346ddfa99d094832aafb5023d2899b6e (1).png


67e0fd3667154345a39661139ae56f08.png



欧克,从上面的四张图片,我们就可以看出,my_qsort函数完美的成功实现,小编在自

己实现这个函数的过程中,遇到了4个bug,每个都能让我生不如死😣😣😣


下面给大家,说说我遇到的bug吧,这样你们在自己编写代码时,就可以不用犯小编遇到的错误了,更加节省你们的时间,少走些弯路,嘻嘻🤭🤭🤭


1.结构体的声明放在使用结构体函数的下面,一定要把类型声明放在cmp_by_struct_age函数声明的上面


2.my_qsort函数内部的for循环结构的判断条件要设置好


3.my_qsort函数内部,我们再回调函数时,要注意传过去的地址,因为不同元素单个个体的地址大小是不同的,所以我们要用下面这样的传地址方式


swap((char*)base + j*width, (char*)base + (j+1)*width, width);
if (ps((char*)base + j*width, (char*)base + (j+1)*width) > 0)

4.my_qsort函数的第二和第三个参数分别是,数组的元素个数(记住是元素个数,比如一个结构成员,一个浮点数,一个整型,都是一个元素)和单个元素的字节大小(记住是字节大小,也就是1,2,3,4这些大小,是整数)

相关文章
|
15天前
|
存储 C语言
【C语言基础】一篇文章搞懂指针的基本使用
本文介绍了指针的概念及其在编程中的应用。指针本质上是内存地址,通过指针变量存储并间接访问内存中的值。定义指针变量的基本格式为 `基类型 *指针变量名`。取地址操作符`&`用于获取变量地址,取值操作符`*`用于获取地址对应的数据。指针的应用场景包括传递变量地址以实现在函数间修改值,以及通过对指针进行偏移来访问数组元素等。此外,还介绍了如何使用`malloc`动态申请堆内存,并需手动释放。
|
15天前
|
存储 编译器 C语言
【C语言基础考研向】09 一维数组
数组是一种有序集合,用于存储相同类型的数据,便于统一操作与管理。例如,将衣柜底层划分为10个格子存放鞋子,便于快速定位。在C语言中,数组定义格式为 `类型说明符数组名[常量表达式];`,如 `int a[10];` 表示定义了一个包含10个整数的数组。数组初始化时可以直接赋值,也可以部分赋值,且数组长度必须固定。数组在内存中连续存储,访问时需注意下标范围,避免越界导致数据异常。数组作为参数传递时,传递的是首地址,修改会影响原数组。
|
15天前
|
存储 C语言
【C语言基础考研向】10 字符数组初始化及传递和scanf 读取字符串
本文介绍了C语言中字符数组的初始化方法及其在函数间传递的注意事项。字符数组初始化有两种方式:逐个字符赋值或整体初始化字符串。实际工作中常用后者,如`char c[10]=&quot;hello&quot;`。示例代码展示了如何初始化及传递字符数组,并解释了为何未正确添加结束符`\0`会导致乱码。此外,还讨论了`scanf`函数读取字符串时忽略空格和回车的特点。
|
18天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
18天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第六章 数组_一维数组_二维数组_字符数组详解
本章介绍了C语言中的数组概念及应用。数组是一种存储同一类型数据的线性结构,通过下标访问元素。一维数组定义需指定长度,如`int a[10]`,并遵循命名规则。数组元素初始化可使用 `{}`,多余初值补0,少则随机。二维数组扩展了维度,定义形式为`int a[3][4]`,按行优先顺序存储。字符数组用于存储字符串,初始化时需添加结束符`\0`。此外,介绍了字符串处理函数,如`strcat()`、`strcpy()`、`strcmp()` 和 `strlen()`,用于拼接、复制、比较和计算字符串长度。
|
24天前
|
存储 安全 C语言
C语言 二级指针应用场景
本文介绍了二级指针在 C 语言中的应用,
|
1月前
|
搜索推荐 C语言
指针与数组
指针与数组
50 9
|
1月前
|
存储 编译器 数据处理
【编程秘籍】解锁C语言数组的奥秘:从零开始,深入浅出,带你领略数组的魅力与实战技巧!
【8月更文挑战第22天】数组是C语言中存储同类型元素的基本结构。本文从定义出发,详述数组声明、初始化与访问。示例展示如何声明如`int numbers[5];`的数组,并通过下标访问元素。初始化可在声明时进行,如`int numbers[] = {1,2,3,4,5};`,编译器自动计算大小。初始化时未指定的元素默认为0。通过循环可遍历数组,数组名视为指向首元素的指针,方便传递给函数。多维数组表示矩阵,如`int matrix[3][4];`。动态数组利用`malloc()`分配内存,需用`free()`释放以避免内存泄漏。掌握这些技巧是高效数据处理的基础。
55 2
|
1月前
|
存储 编译器 C语言
【C语言篇】深入理解指针2
代码 const char* pstr = "hello world."; 特别容易让初学者以为是把字符串 hello world.放 到字符指针 pstr ⾥了,但是本质是把字符串 hello world. 首字符的地址放到了pstr中。
|
1月前
|
存储 程序员 C语言
指针的高级应用:指针数组、数组指针、函数指针等。
指针的高级应用:指针数组、数组指针、函数指针等。
71 0