C语言之static误用导致的问题
前段时间,我在为步进电机撰写驱动程序的时候,遇到了一个问题。当时是做一个比赛,所以时间比较匆忙,我就没有仔细考虑,为了求快,导致写代码比较随意。然后就写出了一个bug,我当时是没有办法解决因为我很急,而且事情很多。现在事情过去了,我就写一个总结。
当时我在motor.h头文件里写了大致如下的代码。
1 | // ... |
然后我就发现一个问题,编译器告诉我,g_motor1重定义,我当时觉得是因为我没有加header guard,然后我就加了。
之后就发生了一个神奇的事情,编译器不再抱怨我的代码有错误,但是我就是不能在中断里面通过全局变量来修改状态。之后,我尝试各种方法都不行。然后我利用ST-LINK进行单步调试,发现同样叫g_motor1的变量,即使是在函数里没有改变这个变量,只是读取,但是在进出函数时候有两个状态。
这个时候,我想到可能有两个都叫做g_motor1的变量,于是就写了一个demo来测试和印证我的想法。下面是具体的代码。
motor.h的内容
1 |
|
motor.c的内容
1 |
|
main.c的内容
1 |
|
事实却是是如此,输出内容如下。
1 | Motor1 speed: 300 |
完全和预期不一样,我想应该大多数人和我一样,都觉得g_motor1.speed = 400;会改变motor_init函数内的输出内容。实际上并不是这样的,通过观察汇编,发现对于g_motor1一个地址是02FA000h,另外一个是02FA00Ch。
这就说明,一个名字可能是有两个变量。造成这样的原因就是因为static的误用。
在我的固有印象里,如果在头文件里写下面这样的代码,是会重定义的。
1 | void motor_set_speed(step_motor_t* motor, int32_t speed) |
为此,我们通常会为函数加上static修饰,从而能够避免重定义,因为static可以把范围框定在文件内。通过VS2022来观察,会发现不管是在motor.c还是main.c内调用的motor_set_speed的函数地址是一样的。于是,就很容易推论到变量上也是一样的来解决重定义问题,但是实际上会导致变量变为多份。
这根本原因是在于c文件#include的时候,头文件中的static变量每次被拷贝到一个c文件里,就会产生新的一份,而且他们的名字都一样。
因此就总结出来一条经验,禁止在头文件内定义static变量,而是按照下面这样的方法,在头文件中extern,然后在源文件中定义static变量。
motor.h的内容。
1 |
|
motor.c的内容。
1 |
|
一般来说,我都尽量不使用全局变量,但是在嵌入式开发中,因为中断函数没有办法传参,所以只能通过全局变量来解决。
