[C语言低能儿]函数疯装进阶-static与函数指针
static关键字
印象中以前C语言老师没有把static讲全,只是简单讲了static用于修饰局部变量。
但是在后续的学习过程中,发现static并非仅有一种用法。
作用于局部变量
这是很多人常用的了
在任意一个函数内部定义的变量(不加static),其初始值不确定,并存放于栈区,出了这个函数就不能确定其值。
而对于用static修饰的局部变量,编译器会将其初始化为零,和全局变量一同储存,并且static所在的语句只会执行一次。
作用于全局变量
全局变量定义于函数体外部,对于全工程都可见,其他文件使用关键字extern声明之后即可使用。
但是如果在其前面添加static关键字,那么该静态全局变量将仅对当前文件可见,其他文件不可访问。
作用于函数
和作用于全局变量类似的,静态函数只能在声明它的文件可见,其他文件不能引用该函数。
对于封装的益处
使用static变量后,我们可以将我们自己函数库封装中的一些不希望外部调用的函数和变量隐藏起来,同时如果外部定义了同名的函数或者变量,编译器也不会报错
如果像是这么写的话,在库外面就不能再定义a
这个变量了,如果进行编译会出现如下报错:
foriver@DESKTOP-T9PMSMI:~/code/C/staticNfun$ gcc main.c staticvar.c -I .
/usr/bin/ld: /tmp/cc0WGFQn.o:(.bss+0x0): multiple definition of `a'; /tmp/cce6Zp1k.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
但是如果在a
前面添加static修饰的话就没有问题了
当然函数方面也是同理。
函数指针
很多时候底层的函数各有各的不同,比如STM32的HAL库需要修改GPIO的电平值需要三个参数,但是有的单片机的sdk里写的就可能只需要两个参数。
如果想要写一个上层库去做封装的话,不得不把移植性考虑进去。
那能这一次写好了在32的版本,下一次移植到其他平台上就要进到库里一句一句的改吧?
怎么去让自己的封装更加的灵活呢,这里就会用到函数指针了。
实际上如果你去阅读HAL库这种比较成熟的库会发现有很多使用函数指针的地方。
声明规则
例子:
void *fun(void *param)
这里fun
是一个指向函数的指针,同时它有一个void指针作为入口参数
可以自行查阅资料,另外这是我之前的总结
https://www.cnblogs.com/Foriver/p/15888594.html
使用例
笔者在之前进行小车配合mpu6050yaw角控制方向的时候,遇到过因为yaw角范围限定在-180~180导致pid计算出现问题的情况
我这里想到的一个解决方法是修改yaw角的加法法则,把关于yaw角的加减计算替换成自己的加法法则,这样就能一次性的解决:
float yaw_calc_plus(float num1, float num2)
{
float res = num1 + num2;
if (res > 180)
res = res - 360;
else if (res < -180)
res = 360 + res;
return res;
}
当时遇到一个问题就是我的pid已经封装好了,但是关于误差的运算也需要替换加法法则,怎么办呢,总不能再写一个pid吧?(如果你看过我的代码的话会发现我真的这么干了)
这里我就想到了使用函数指针:
在pid结构体中加入这么一个成员:
float (*pid_add_f)(float a,float b);
在普通的pid计算中我们将其赋值为NULL,当想要替换加法法则时则可以将其赋值为我们想调用的函数,下面就是我这次智能车比赛的代码片段:
PID_AddFuncSet(&yawpid, &yaw_add_fun);//这是设置时调用的函数
void PID_AddFuncSet(PID_t *pid, float (*pid_add_f)(float a, float b))
{
pid->pid_add_f = pid_add_f;
}
在pid计算中就只需要稍微修改就可以:
if (pid->pid_add_f != NULL)
pid->err = pid->pid_add_f(target, -current);//添一个负号就是减法
else//如果指针为NULL那就运行正常的减法
pid->err = target - current;