C语言——函数指针和函数指针数组

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

函数指针是指向函数的指针变量,它可以用来存储和调用函数的地址。函数指针在C语言中有着广泛的应用,以下是几种常见的用途:

回调函数:函数指针可以作为参数传递给其他函数,使得被调用的函数能够在特定条件或事件发生时,调用传入的函数指针进行回调操作。这样可以实现函数之间的灵活交互和扩展,常见于事件处理、消息传递等场景。

函数指针数组:可以使用函数指针数组来实现多态性或动态分派。通过在数组中存储不同的函数指针,并根据需要调用其中的函数,可以在运行时决定调用哪个函数,从而实现动态切换功能。

函数指针作为返回值:函数指针也可以作为函数的返回值,这样可以根据函数的不同条件或逻辑,在运行时返回不同的函数指针,实现动态的函数调用。

函数指针作为数据结构的成员:函数指针可以作为结构体或对象的成员,用来保存某个特定的函数地址。这样可以实现一些高级的数据结构,如函数回调链表、状态机等,提供更灵活的功能扩展。

动态加载库函数:在程序运行时,可以使用函数指针动态地加载和调用外部的共享库函数。这样可以实现插件式开发、动态扩展等功能。


提示:以下是本篇文章正文内容,下面案例可供参考

一、初步探讨

在C语言中,函数指针的声明和使用可以使用以下格式:
*返回类型 (指针变量名)(参数列表);其中,返回类型是函数的返回类型,指针变量名是函数指针的名称,参数列表是函数的参数列表。
我们先看一段代码,体会一下函数指针的指哪打哪:

#include <stdio.h>

void sayHello() {
    printf("Hello!\n");
}

void sayGoodbye() {
    printf("Goodbye!\n");
}

int main() {
    // 声明一个函数指针,指向无返回值且无参数的函数
    void (*functionPtr)();

    // 将函数指针指向sayHello函数
    functionPtr = sayHello;
    // 通过函数指针调用sayHello函数
    functionPtr();

    // 将函数指针指向sayGoodbye函数
    functionPtr = sayGoodbye;
    // 通过函数指针调用sayGoodbye函数
    functionPtr();

    return 0;
}

在这里插入图片描述
在上面的示例中,我们声明了一个函数指针**functionPtr**,它指向无返回值且无参数的函数。我们将functionPtr分别指向sayHellosayGoodbye函数,并使用函数指针调用这两个函数。

2,函数指针数组:

我们来看一下一个函数指针数组的案例,这里函数指针的用途是转移表,应用于一个计算器:
我们先来看一段代码:

#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", 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;
}

上面代码中,我们不难看到,case语句中有很多是重复的,这叫做代码冗余,而工作中一般是不能出现这种状况的,这里的话,我们可以试着用函数指针数组来简化一下代码,修改如下:

#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; 
    int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表 
    while (input) 
    { 
         printf( "*************************\n" ); 
         printf( " 1:add           2:sub \n" ); 
         printf( " 3:mul           4:div \n" ); 
         printf( "*************************\n" ); 
         printf( "请选择:" ); 
     scanf( "%d", &input); 
         if ((input <= 4 && input >= 1)) 
        { 
         printf( "输入操作数:" ); 
             scanf( "%d %d", &x, &y); 
             ret = (*p[input])(x, y); 
        } 
         else 
              printf( "输入有误\n" ); 
         printf( "ret = %d\n", ret); 
    } 
     return 0; 
} 

经过简化后,把可能会用到的函数放进函数指针数组里面,这样就方便很多了。 int(*p[5])(int x, int y),这里的p先和[]结合,说明是p是一个数组,而数组的内容是int (*)(int ,int )类型的函数指针。

3。指向函数指针数组的指针

我们知道函数指针数组的指针是一个指针,那么,指针指向一个数组,数组里面的元素都是函数指针,那么我们怎么定义这个指函数指针数组的指针呢?
我们来看一段代码就知道了:

void test(const char* str) 
{ 
printf("%s\n", str); 
} 
int main() 
{ 
//函数指针pfun 
void (*pfun)(const char*) = test; 
//函数指针的数组pfunArr 
void (*pfunArr[5])(const char* str); 
pfunArr[0] = test; 
//指向函数指针数组pfunArr的指针ppfunArr 
void (*(*ppfunArr)[5])(const char*) = &pfunArr; 
return 0; 
}

4,这里还可以记录一下回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个
函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数
的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进
行响应。我们通过对比以下两个函数来体会一下回调函数。
首先演示一下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; 
} 

在这里插入图片描述
接下来我们使用回调函数,模拟实现qsort(采用冒泡的方式)。
我们这里有用到void*的指针,
void * 以定义一个指针变量,但不说明它指向哪一种类型数据.
1.传参:通用类型
可以作为函数模板,链表等参数的通用参数。在使用时,只需要强制类型转换就可以。
2.强制类型转换
有时候由于重载等的干扰,导致需要转换成void *,来进行取地址。
例如,(void *)obj.member,就可以取到member的地址;直接&(obj.member)取到的实际上是obj的开始地址。
3.指向0的地址
(void *)0,指向全是0的地址,相当于NULL。(参考至:

https://blog.csdn.net/b1480521874/article/details/83010304


#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 }; 
   //char *arr[] = {"aaaa","dddd","cccc","bbbb"}; 
   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; 
}

最后的结果是一样的,口头上比较难解析,感兴趣的可以观看:比特鹏哥
总结:
浅谈,印象不深,但应该能懂,后面补一个文章以简单的输出来区分一下,帮助理解。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值