文章目录
- 一、前言
- 二、指针函数
- 2.1 概念
- 2.2 定义
- 2.3 具体例子
- 三、函数指针
- 3.1 概念
- 3.2 定义
- 3.3 具体例子
- 3.4 回调函数
- 3.4.1 概念
- 3.4.2 例子1
- 3.4.3 例子2
- 四、函数指针数组
- 4.1 概念
- 4.2 定义
- 4.3 具体例子
- 五、函数指针数组的指针
- 5.1 概念
- 5.2 定义
- 5.3 具体例子
一、前言
关于指针和函数文章从有两方面入手,一方面是将指针作为实参传给函数;另一方面则是函数指针这一类函数和指针结合的数据类型。
科普
函数名本质上是函数的地址,其用法跟数组名类似。
二、指针函数
2.1 概念
指针函数本质上是一个函数,此函数返回一个指针。
2.2 定义
char *func(char a, char b){}
2.3 具体例子
在函数指针的使用中,大多数情况下需要动态分配内存来存放返回的数据。如果使用局部变量,当函数运行结束后,局部变量会被释放,上层代码访问时可能会导致非法访问,从而引发程序段错误。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>char *my_strcat(char *str1, char *str2)
{char *p = NULL;char *p1 = NULL;char *ptemp = NULL;/* 动态内存分配 */p = (char *)malloc(20 * sizeof(char));if(!p){return NULL;}p1 = p;ptemp = str1;while(*ptemp != '\0'){*p1++ = *ptemp++;}ptemp = str2;while(*ptemp != '\0'){*p1++ = *ptemp++;}*p1 = '\0';printf("p=%s\n", p);return p;
}int main()
{char str1[10] = "hello ";char str2[10] = "world!";char *return_str = NULL;return_str = my_strcat(str1, str2);printf("return str[%s]\n", return_str);if(return_str)free(return_str);return 0;
}
三、函数指针
3.1 概念
函数指针本质是一个指针,它指向一个函数的地址,通过它来调用函数。
3.2 定义
首先定义一个函数:
int add(int x, int y){}
此时,函数的地址就是函数名。我们声明一个函数指针并把函数的地址赋值给这个函数指针。这里要注意,定义的函数和声明的函数指针的参数列表应该保持一致。
int (*pf)(int, int) = add;
下面让我们看一个表达式:
(*(void(*)())0)();
这一行代码的意思是:调用0地址处的函数,该函数无参,返回类型是void。
我们再来看一下signal函数的原型
void (*signal(int, void(*)(int)))(int);
函数名:signal
两个参数:int和void(*)(int) (第二个参数也是一个函数指针)
返回值:void(*)(int)(函数指针)
这样看起来很复杂,我们可以使用typedef来简化表达。
首先,我们可以把signal函数的原型写成
void(*)(int) signal(int, void(*)(int));
但是,这种结构语法是不允许,C语言的语法要求函数的返回类型必须明确地写在函数名之前。接下来我们使用typedef简化。
typedef void(*pfun_t)(int);//void(*)(int) pfun_t
pfun_t signal(int, pfun_t);//pfun_t -> void(*)(int)
3.3 具体例子
int add(int a, int b) { return a + b; }int main()
{int (*pf)(int, int) = add;int ret = pf(3,5);printf("ret=%d\n", ret);return 0;
}
3.4 回调函数
3.4.1 概念
回调函数是一种通过函数指针调用的函数。
3.4.2 例子1
Handle函数第二个参数是该函数的首地址,把这个首地址用函数指针接收int (*Callback)(int),在Handle函数中使用这个首地址,Callback(x),这时就会运行主函数Handle第二个参数的函数。
int Callback_1(int a) ///< 回调函数1
{printf("Hello, this is Callback_1: a = %d ", a);return 0;
}int Callback_2(int b) ///< 回调函数2
{printf("Hello, this is Callback_2: b = %d ", b);return 0;
}int Callback_3(int c) ///< 回调函数3
{printf("Hello, this is Callback_3: c = %d ", c);return 0;
}int Handle(int x, int (*Callback)(int)) ///< 注意这里用到的函数指针定义
{Callback(x);
}int main()
{Handle(4, Callback_1);Handle(5, Callback_2);Handle(6, Callback_3);return 0;
}
3.4.3 例子2
这是一个常用在工作中的使用形式。M26_WorkStatus_TypeDef的第二个参数保存的函数的首地址。
/********* 工作状态处理 *********/
typedef struct
{uint8_t mStatus;uint8_t (* Funtion)(void); //函数指针的形式
} M26_WorkStatus_TypeDef; //M26的工作状态集合调用函数/**********************************************
** >M26工作状态集合函数
***********************************************/
M26_WorkStatus_TypeDef M26_WorkStatus_Tab[] =
{ {GPRS_NETWORK_CLOSE, M26_PWRKEY_Off }, //模块关机{GPRS_NETWORK_OPEN, M26_PWRKEY_On }, //模块开机{GPRS_NETWORK_Start, M26_Work_Init }, //管脚初始化{GPRS_NETWORK_CONF, M26_NET_Config }, //AT指令配置{GPRS_NETWORK_LINK_CTC, M26_LINK_CTC }, //连接调度中心 {GPRS_NETWORK_WAIT_CTC, M26_WAIT_CTC }, //等待调度中心回复 {GPRS_NETWORK_LINK_FEM, M26_LINK_FEM }, //连接前置机{GPRS_NETWORK_WAIT_FEM, M26_WAIT_FEM }, //等待前置机回复{GPRS_NETWORK_COMM, M26_COMM }, //正常工作{GPRS_NETWORK_WAIT_Sig, M26_WAIT_Sig }, //等待信号回复{GPRS_NETWORK_GetSignal, M26_GetSignal }, //获取信号值{GPRS_NETWORK_RESTART, M26_RESET }, //模块重启
}
/**********************************************
** >M26模块工作状态机,依次调用里面的12个函数
***********************************************/
uint8_t M26_WorkStatus_Call(uint8_t Start)
{uint8_t i = 0;for(i = 0; i < 12; i++){if(Start == M26_WorkStatus_Tab[i].mStatus){ return M26_WorkStatus_Tab[i].Funtion();}}return 0;
}
四、函数指针数组
4.1 概念
函数指针数组本质是一个数组,数组中存放的函数指针。
4.2 定义
int (*pfArr[2])(int, int);
在函数指针的基础上,在函数名后面加上[]。
4.3 具体例子
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 division(int a, int b) { return a / b; }int main()
{/* 模拟键盘输入 */int input = 2;int ret;int (*pf[5])(int, int) = {NULL, add, sub, mul, division};ret = pf[input](3,5);printf("ret=%d\n", ret);return 0;
}
五、函数指针数组的指针
5.1 概念
函数指针数组的指针本质上是一个指针,指向一个函数指针数组。
5.2 定义
int(* (*p3)[4])(int, int);
在函数指针数组的基础上,在函数名前面加上*号并用小括号括起来。
5.3 具体例子
在这里面,一定是&pf才行,因为p1是指向数组的指针而不是指向数组首地址的指针,我们应该使用&把数组的地址取出来。
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 division(int a, int b) { return a / b; }int main()
{/* 模拟键盘输入 */int input = 2;int ret;int (*pf[5])(int, int) = {NULL, add, sub, mul, division};int (*(*p1)[5])(int, int) = &pf;ret = (*p1)[input](3,5);printf("ret=%d\n", ret);return 0;
}