函数的基本表示方法
函数定义的语法如下:
类型 函数名 (形式参数)
{
代码块
}
形式参数主要包括变量名和类型声明;代码块主要是局部变量和函数调用时需要执行的语句。
函数指针和变量指针是一个意思,都是一个地址。不同是函数是代码块的集合,某一位置的连续值,实现的是一种方法,变量是某一位置的一个确定的值。
基于这两种元素的共同特性,我们可以将其集成在一个结构体里面,变成我们C++常说的类。
指针数组和数组指针
指针数组
指针数组就是一个数组,数组的元素存放的是指针类型的元素。
uint8_t* pBuffer[31]
//优先级问题:[]的优先级比*高
//说明pBuffer是一个数组,而uint8_t*是数组里面的内容
//这句话的意思就是:pBuffer是一个含有31和uint8_t*的数组
数组指针
数组指针就是一个指针,该指针指向一个数组。
uint8_t (*pBuffer)[31];
//由于[]的优先级比*高,因此在写数组指针的时候必须将*pBuffer用括号括起来
//pBuffer先和*结合,说明pBuffer是一个指针变量
//这句话的意思就是:指针pBuffer指向一个大小为31个无符号整型的数组。
一维数组参数传递
比如我有一个函数:
我们定义pBuffer[]为一个一维数组,
void Fun1(uint8_t *pBuffer)
{
}
我们可以看到上述*pBuffer实际上就是一个数据类型为uint8_t的数组指针。
数组传参时,会退化为指针,将整个数组拷贝一份传入函数时,将数组名看做常量指针,传数组首元素的地址。
传值调用:函数调用参数是采用拷贝该参数的方式,函数对参数的修改是修改拷贝的参数值,不对原参数进行修改。
传址调用:被传递参数是数组名pBuffer,函数中使用下标引用该数组的参数,比如pBuffer[i],那么函数对参数的修改实际上修改的是调用程序的数组元素,数组不会复制。
但数组所谓的传址调用也可以看作传值调用,数组名的值实际上就是一个指针,指针指向的地址,下标引用其实是间接访问的一种方式,但最终结果也没有复制数组的元素。
故有两个结论:函数的参数传递时传值调用;传递给函数的数组在行为上像传址调用。
故对于一个数组pBuffer[31]来说,其参数的传递可以有以下几种表示方法。
//用数组的形式传递参数,不需要指定参数的大小,因为在一维数组传参时,形参不会真实的创建数组,传的只是数组首元素的地址。(如果是变量的值传递,那么形参就是实参的一份拷贝)
void Fun(int pBuffer[])
{}
//不传参数可以,传递参数也可以
void Fun(int pBuffer[10])
{}
//一维数组传参退化,用指针进行接收,传的是数组首元素的地址
void Fun(int *pBuffer)
{}
//*pBuffer[31]是指针数组,传过去的是数组名
void Fun(int *pBuffer[31])
{}
函数指针的一种应用
函数指针的形式:类型(*)( ),例如:int (*p)( ).它可以存放函数的地址。
这里用一个实例进行说明,拿串口数据处理函数为例。
第一步:
我们将串口数据处理定义为一个“类”,这个类就是用结构体来表示:
typedef struct DataTypeDef
{
int a;
char b;
void (*Data_callback1)(uint8_t *);
void (*Data_callback2)(uint8_t *pBuffer);//其他指针函数
void (*Data_callback3)(uint8_t *pBuffer);//
}DataTypeDef;
我们为了方便引用该函数,声明一个该函数的指针:
void (*Data_Callback)(uint8_t *)
这里也要注意:在申明函数原型时形参是否添加参数的名字,原则上并不是必须,但是在函数原型中添加描述性参数名可以帮助用户更快的对程序进行理解。
因此该函数指针我们也像声明串口处理函数那样改写为:
void (Data_Callback)(uint8_t *pBuffer)
第二步:
方法函数编写
void QData_Callback1(uint8_t *pBuffer)
{
}
void QData_Callback2(uint8_t *pBuffer)
{
}
void QData_Callback3(uint8_t *pBuffer)
{
}
第三步:
绑定类和方法
void Binding(DataTypedef* member)
{
member-> Data_Callback1 = QData_Callback1;
member-> Data_Callback2 = QData_Callback2;
member-> Data_Callback3 = QData_Callback3;
}
第四步:
创建一个对象
DataTypeDef DataStruct;
bind(&DataStruct);
这样直接引用结构体的指针函数就可以实现面向对象的引用了。
DataStruct.Data_CallBack1(pBuffer);