目录
6、传递指针给函数:通过引用或地址传递参数,使传递的参数在调用函数中被改变。
一、指针
1、指针
指针:指向(/存储)其他变量的地址,该变量的数据类型需与指针的数据类型保持一致。
等价于:
int *p = &var_runoob;
2、野指针与空指针
野指针:没有为指针附地址,此时指针任意指向一个地址,不利于程序安全。
空指针:指针附NULL,地址为0x0。可用于if判断。
注:free()内存后指针赋 NULL。
3、指针的算术运算
对指针进行四种算术运算:++、--、+、- ;指向下一个元素的存储单元,跳跃的字节数取决于所指向变量数据类型长度。
例:
int a[5];
int *p;
p = a;
则*p = a[0], *(p+1) = a[1]
4、指针数组:(多个地址)由指针变量组成的数组。
声明:[]的优先级高于*,因此先是数组,后为指针数组。
const char *names[] = {
"Zara Ali",
"Hina Ali",
"Nuha Ali",
"Sara Ali",
};
int i = 0;
for ( i = 0; i < MAX; i++)
{
printf("Value of names[%d] = %s\n", i, names[i] );//字符型指针数组不用*names[i]
}
区分于数组指针:(一个地址),该指针指向数组,但指向的数组占多少个字节不确定,长度固定(32 位系统下占 4 个字节)。
5、多级指针:C 允许指向指针的指针。
二级指针存一级指针的地址,即二级指针指向一级指针。
三级指针存二级指针的地址,即三级指针指向二级指针。
6、传递指针给函数:通过引用或地址传递参数,使传递的参数在调用函数中被改变。
7、从函数返回指针(地址)
返回静态局部变量地址:
由于函数中的局部变量存储在内存的栈区,当函数调用结束后,局部变量所占的内存地址便被释放了。
而static 变量的值存放在内存中的静态数据区,不会随着函数执行的结束而被清除,故能返回其地址。
声明:
int * getRandom()
二、函数指针
函数指针:指向函数的指针。
1、声明
int (*fun_ptr)(int,int); //声明一个指向同样参数、返回值的函数指针类型
2、定义
fun_ptr=fun;
3、调用:可直接用指针调用。
fun_ptr(a,b);
例:
typedef void(*FunType)(int);
//typedef关键字,定义一个名为FunType函数指针类型,而不是一个FunType变量。
//形式同 typedef int* PINT;
void myFun(int x);
void hisFun(int x);
void herFun(int x);
void callFun(FunType fp,int x);
int main()
{
callFun(myFun,100);//传入函数指针常量,作为回调函数
callFun(hisFun,200);
callFun(herFun,300);
return 0;
}
void callFun(FunType fp,int x)
{
fp(x);//通过fp的指针执行传递进来的函数,注意fp所指的函数有一个参数
}
void myFun(int x)
{
printf("myFun: %d\n",x);
}
void hisFun(int x)
{
printf("hisFun: %d\n",x);
}
void herFun(int x)
{
printf("herFun: %d\n",x);
}
三、回调函数
1、回调函数就是一个通过函数指针调用的函数。
函数指针作为某个函数的参数:不同功能的函数有相同的形式,将需要调用的函数指针作为参数传递给一个函数,在函数内使用指针调用对应函数。
回调函数最大的意义在于解耦,降低代码之间的耦合度。
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;
}
可以看到,Handle()函数里面的参数是一个指针,在main()函数里调用Handle()函数的时候,给它传入了函数Callback_1()/Callback_2()/Callback_3()的函数名,这时候的函数名就是对应函数的指针,也就是说,回调函数其实就是函数指针的一种用法。
2、回调函数实例(转载,以后可能需要)
一个GPRS模块联网的小项目,使用过的同学大概知道2G、4G、NB等模块要想实现无线联网功能都需要经历模块上电初始化、注册网络、查询网络信息质量、连接服务器等步骤,这里的的例子就是,利用一个状态机函数(根据不同状态依次调用不同实现方法的函数),通过回调函数的方式依次调用不同的函数,实现模块联网功能,如下:
/********* 工作状态处理 *********/
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;
}
所以,如果有人想做个NB模块联网项目,可以copy上面的框架,只需要修改回调函数内部的具体实现,或者增加、减少回调函数,就可以很简洁快速的实现模块联网。