C语言——指针进阶(二)

简介: C语言——指针进阶(二)



七.函数指针数组

说白了函数指针数组就是函数指针名字后面添加[  ],二者结合成为数组。

那么这个函数指针数组到底有什么用呢?先不用着急,下面用一个计算器功能代码为大家抛砖引玉。

这是一个简单的计算器代码,通过选择不同的功能让其进入不同的函数计算数字。

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 Mull(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;
  int ret = 0;
  do
  {
    menu();
    printf("请选择:");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      printf("请输入两个数字:\n");
      scanf("%d %d", &x, &y);
      ret = Add(x, y);
      printf("%d\n", ret);
      break;
    case 2:
      printf("请输入两个数字:\n");
      scanf("%d %d", &x, &y);
      ret = Sub(x, y);
      printf("%d\n", ret);
      break;
    case 3:
      printf("请输入两个数字:\n");
      scanf("%d %d", &x, &y);
      ret = Mull(x, y);
      printf("%d\n", ret);
      break;
    case 4:
      printf("请输入两个数字:\n");
      scanf("%d %d", &x, &y);
      ret = Div(x, y);
      printf("%d\n", ret);
      break;
    case 0:
      printf("退出程序\n");
      break;
    default :
      printf("输入错误,请重新输入:\n");
    }
  } while (input);
  return 0;
}

但这样我们会发现一个问题,当我们尝试添加更多的函数功能时,switch会变得越来越长,更繁杂了。

通过分析代码可以知道,其实每个函数的功能并不相同,但是它们所使用的形参和所代表的函数类型都是一样的。

接下来我们来修改一下代码:

先把switch部分删掉,改成函数指针数组。

可以看到这里面的下标与我们计算器的选择对应不上,这时候可以用NULL来占用数组一个位置。

然后通过条件if语句来实现函数功能就行了。

八.指向函数指针数组的指针

感兴趣可以当作扩展了解一下哈~

指向函数指针数组的指针,怎么说呢本质是指针,那存储对象就应该是函数指针数组的地址了。

而我们需要的指针则由(*p)括起来确保是指针而不是数组,这里pfArr简化成p方便辨认。最后就变成了指向这个数组的指针了。重点是指向数组,而不是变成数组。

九.回调函数

我们回到switch代码部分进行回调函数的实现:我们可以先封装一个函数,再让代码相似部分调用4次即可。

这就是回调函数的神奇之处,用函数指针来接受所需要的函数功能,在calc函数里面使用已经接受了功能函数的地址的指针,输入实参。

进一步理解回调函数:

老规矩,先用简单的例子帮助大家理解,后面再深入了解qsort的妙用。先用一个经典例子:冒泡排序。

其实冒泡排序很简单,10个元素该元素最差跑9次交换,n个元素就跑n-1次。每一次跑完下一个元素都会减少一次对比。

其实这个函数不够通用,它只能排序整型。

接下来我们就来介绍qsort函数,这个通用排序函数。

通过这个我们知道两个相同类型数据的比较方法是不同的。

那么可以推出qsort的第四项形参是提供两个数据的比较方法。我们可以想象把上诉冒泡排序中的9和8分别传输给e1,e2,让它们在cmp里面比较。不过这时候会遇到一个问题,*e1会出错,原因在于void*类型是无法进行解引用的。

void*的真正作用是接受各种类型的指针:

所以如果要实现整型比较大小只能先强制转换了。

cmp的规则是当p1指向的元素>p2指向的元素时返回大于0的数字,相等返回0,小于返回小于0的数字。

运行代码,排序成功。其实qsort最关键的部分是第四个形参,必须得创建这个数据对应类型的比较方法才可以进行,比如整型用<>来比较,结构体可以用字符串长度来比较等等。

下面我们来测试结构体排序:

又到了最关键的创造比较函数部分。

再尝试按照名字比较,这时候的名字是字符串,可不能用减号来表示结果了,可以用到strcmp这个函数来比较字符串,巧合的是strcmpd的返回类型与cmp的返回规则一致。

字符串之间的排序是按照字典顺序比较的,比如abc与aq,一开始两个a相同,但后面的q比b大,所以abc排在aq的前面。

结尾

本文最精彩的一部分就在于qsort排序了,它不同于我们传统的单一类型的排序,它更加多样性,赋予了我们更多排序的思路。

相关文章
|
1月前
|
存储 C语言
【C语言篇】深入理解指针3(附转移表源码)
【C语言篇】深入理解指针3(附转移表源码)
36 1
|
26天前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
45 0
|
25天前
|
C语言
【c语言】指针就该这么学(3)
本文介绍了C语言中的函数指针、typedef关键字及函数指针数组的概念与应用。首先讲解了函数指针的创建与使用,接着通过typedef简化复杂类型定义,最后探讨了函数指针数组及其在转移表中的应用,通过实例展示了如何利用这些特性实现更简洁高效的代码。
15 2
|
26天前
|
C语言
如何避免 C 语言中的野指针问题?
在C语言中,野指针是指向未知内存地址的指针,可能引发程序崩溃或数据损坏。避免野指针的方法包括:初始化指针为NULL、使用完毕后将指针置为NULL、检查指针是否为空以及合理管理动态分配的内存。
|
26天前
|
C语言
C语言:哪些情况下会出现野指针
C语言中,野指针是指指向未知地址的指针,通常由以下情况产生:1) 指针被声明但未初始化;2) 指针指向的内存已被释放或重新分配;3) 指针指向局部变量,而该变量已超出作用域。使用野指针可能导致程序崩溃或不可预测的行为。
|
1月前
|
存储 C语言
C语言32位或64位平台下指针的大小
在32位平台上,C语言中指针的大小通常为4字节;而在64位平台上,指针的大小通常为8字节。这反映了不同平台对内存地址空间的不同处理方式。
|
1月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
1月前
|
存储 C语言
C语言指针与指针变量的区别指针
指针是C语言中的重要概念,用于存储内存地址。指针变量是一种特殊的变量,用于存放其他变量的内存地址,通过指针可以间接访问和修改该变量的值。指针与指针变量的主要区别在于:指针是一个泛指的概念,而指针变量是具体的实现形式。
|
1月前
|
C语言
C语言指针(3)
C语言指针(3)
11 1
|
1月前
|
C语言
C语言指针(2)
C语言指针(2)
13 1