【指针的进阶(2)】——函数指针和函数指针数组

简介: 【指针的进阶(2)】——函数指针和函数指针数组

前言

继上一章【指针的进阶(1)】,继续完善第二章内容


一、函数指针

含义:指向函数的指针

1.1 函数指针的写法

#include<stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  int arr[10] = { 0 };
  printf("%p\n", Add);
  //函数名是函数的地址
  printf("%p\n", &Add);
  //&函数名也是函数的地址
  int(*pf)(int,int) = &Add;//函数指针
  return 0;
}

思路: 把函数地址存起来,存到变量pf里,pf是用来存放函数地址的,所以pf类型是函数指针。

函数指针写法:去掉指针变量pf,剩下的其实就是函数指针类型 int(*)(int,int) 左边是返回类型,右边是参数类型

和数组指针很像,int(ptr)[5]=&arr,去掉指针变量ptr,剩下的就是数组指针类型 int()[5]

1.2 函数指针的用法

可以通过指针间接访问函数,调用这个函数

int Add(int x, int y)
{
  return x + y;
}
int main()
{
  int (*pf)(int, int) = &Add;
  int r = Add(3, 5);
  printf("%d\n", r);
  int m = pf(4, 5);
  printf("%d\n", m);
  return 0;
}

1.3 练习题

int main()
{
  (*(void(*)()) 0)();
}

解读这段代码的意思是:

void( * )() 是函数指针类型

( void( * )() ) 把0强制类型转换成函数指针类型

* (void( * )() ) 是对地址解引用

* (void( * )() ) () 最后面的括号是调用这个函数

其实就是

1.将0强制类型转换成void(*)() 函数指针类型的地址

2.调用0地址处的函数


int main()
{
  void ( * signal(int, void(*)(int) ) ) (int);
  return 0;
}

解读这段代码的意思是:

  • signal是一个函数声明
  • signal(int, void( * )(int) )说明:signal函数有两个参数,第一个参数的类型是int,另一个参数的类型是void( * )(int)函数指针类型,该函数指针指向的函数有一个int类型,返回类型是void
  • signal 函数的返回类型也是void( * )(int)函数指针类型,该函数指针指向的函数有一个int类型,返回类型是void

1.4 typedef简化代码

这段代码太过复杂,解读性不高,可以使用 typedef 可以简化

例如


typedef unsigned int uint;

对unsigned int重命名为uint


typedef int * ptr;

对指针重命名为ptr,int* p1; 等同于 ptr p2;


typedef void(*ptr_t)(int);
ptr_t signal(int, ptr_t);

注意 指针函数重命名是在( * )里面换,如把 void(*)(int)重命名为ptr_t,写法是 void(*ptr_t)(int)。signal 函数的返回类型也是void( * )(int)函数指针类型,所以最后简写成了ptr_t signal(int, ptr_t);

二、函数指针数组

数组的每个元素是函数指针类型

类比

  • int * arr[5]; 整型指针数组
  • char * arr2[5]; 字符指针数组

2.1 函数指针数组的写法

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(*pf1)(int,int)= Add;
  int(*pf2)(int,int) = Sub;
  int(*pf3)(int,int) = Mul;
  int(*pf4)(int,int) = Div;
  return 0;
}

存放四个函数的函数指针的类型都是一样的,使用函数指针数组更简便

写法如下:

int main()
{
  int(*pfarr[4])(int, int) = { Add,Sub,Mul,Div };
  return 0;
}

2.2 函数指针数组的用处

写一个计算器,有加减乘除功能,一般的写法如下:

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;
}
void menu()
{
  printf("***************************\n");
  printf("*****  1.add  2.sub  ******\n");
  printf("*****  3.mul  4.div  ******\n");
  printf("*****  0.exit        ******\n");
  printf("***************************\n");
}
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("请输入两个操作数:");
      scanf("%d %d", &x, &y);
      ret = Add(x, y);
      printf("ret = %d\n", ret);
      break;
    case 2:
      printf("请输入两个操作数:");
      scanf("%d %d", &x, &y);
      ret = Sub(x, y);
      printf("ret = %d\n", ret);
      break;
    case 3:
      printf("请输入两个操作数:");
      scanf("%d %d", &x, &y);
      ret = Mul(x, y);
      printf("ret = %d\n", ret);
      break;
    case 4:
      printf("请输入两个操作数:");
      scanf("%d %d", &x, &y);
      ret = Div(x, y);
      printf("ret = %d\n", ret);
      break;
    case 0:
      printf("退出计算器\n");
      break;
    default:
      printf("选择错误,重新选择\n");
      break;
    }
  } while (input);
  return 0;
}

mani函数里有很多重复性的代码,过于冗余,如果要添加更多的函数进去,就要写更多的case语句,工作量大又麻烦。

而使用函数指针数组将大大提高效率

用函数指针数组的方式实现

思路:函数指针数组的使用 - 也叫转移表,意思是函数的下标对应一个数字,通过下标找到某个函数地址,继而调用某个函数。

在使用计算器的时候,通常会出现三种情况,第一种不想使用了,想退出计算器,第二种选择了正确的数字(函数的下标),实现计算;第三种选了大于函数下标的数字,则要重新选择。

对于三种情况,显然用if,else if,else语句最合适不过,因为数组的下标是从0开始的,那么可以让NULL占了0的下标,让函数从下标1开始,会更方便选择。

int (* pfArr[5])(int, int) = {NULL, Add, Sub, Mul, Div};
  //                            0     1    2    3    4

先选择一个数字,如果数字大于0小于下标数字,则进入循环

if (input >= 1 && input <= 4)

再输入两个数字,进行计算,打印。

printf("请输入两个操作数:");
      scanf("%d %d", &x, &y);
      ret = pfArr[input](x, y);
      printf("ret = %d\n", ret);

如果选择的数字为0,则退出计算器;

else if(input == 0)
    {
      printf("退出计算器\n");
    }

如果选的数字大于下标数字,则选择错误,重新选择。

else
    {
      printf("选择错误,重新选择\n");
    }

想选择多次计算的话,把if,else if,else语句放在dowhile()循环里面进行,整个过程就可以循环起来。

具体代码如下:

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;
}
//...可以添加更多的函数
void menu()
{
  printf("***************************\n");
  printf("*****  1.add  2.sub  ******\n");
  printf("*****  3.mul  4.div  ******\n");
  printf("*****  0.exit        ******\n");
  printf("***************************\n");
}
int main()
{
  int input = 0;
  int x = 0;
  int y = 0;
  int ret = 0;
  int (* pfArr[5])(int, int) = {NULL, Add, Sub, Mul, Div};
  //                            0     1    2    3    4
  do
  {
    menu();
    printf("请选择:>");
    scanf("%d", &input);
    if (input >= 1 && input <= 4)
    {
      printf("请输入两个操作数:");
      scanf("%d %d", &x, &y);
      ret = pfArr[input](x, y);
      printf("ret = %d\n", ret);
    }
    else if(input == 0)
    {
      printf("退出计算器\n");
    }
    else
    {
      printf("选择错误,重新选择\n");
    }
  } while (input);
  return 0;
}

总结

本章对函数指针函数指针数组进行了详细地讲解,如果对您有帮助的话,不妨来一个关注吧!

相关文章
|
20天前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
30 3
|
19天前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
30 2
|
28天前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
32 1
|
1月前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。
|
1月前
|
存储
如何通过指针数组来实现二维数组?
介绍了二维数组和指针数组的概念及其区别,详细讲解了如何使用指针数组模拟二维数组,包括定义与分配内存、访问和赋值元素、以及正确释放内存的步骤,适用于需要动态处理二维数据的场景。
|
1月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
1月前
魔法指针 之 二级指针 指针数组
魔法指针 之 二级指针 指针数组
19 1
|
1月前
魔法指针 之 函数指针 回调函数
魔法指针 之 函数指针 回调函数
14 0
|
1月前
|
存储
一篇文章了解区分指针数组,数组指针,函数指针,链表。
一篇文章了解区分指针数组,数组指针,函数指针,链表。
19 0
|
1月前
|
编译器 C语言
【C语言】指针篇-深入探索数组名和指针数组- 必读指南(2/5)
【C语言】指针篇-深入探索数组名和指针数组- 必读指南(2/5)