1、静态全局变量
“static + 全局变量”就定义了一个静态全局变量。静态全局数据有以下特点:
(1)未经初始化时,程序自动初始化为0;
(2)在全局数据区分配内存
静态全局变量包括后边的静态局部变量都是在全局数据区分配内存。一般情况下程序中新产生的动态数据存放在堆区,函数内部的自动变量存放在栈区。自动变量一般会随着函数的退出而释放空间。而全局数据区的数据并不会因为函数的退出而释放空间。
(3)作用域是定义它的文件
全局变量可以在工程所有文件中共享使用,作用域是整个工程。在一个文件中定义的全局变量,可以在另一个文件中通过“extern+全局变量”的方式进行声明后使用这个变量。而静态全局变量只能在定义它的文件中使用,其他文件即使通过extern声明也不能使用。这样静态全局变量有一个好处:可以在需要的文件中随便定义,而不必考虑其他文件中是否有重名的变量从而造成冲突。
2、静态局部变量
和静态全局变量的定义类似,“static + 局部变量”就定义了静态局部变量。和静态全局变量类似,静态局部变量具有如下特点:
(1)在全局变量区分配内存;
(2)一般在声明处初始化,没有显式初始化时自动初始化为0;
(3)在程序执行到声明处时首次初始化,以后不再进行初始化;
静态局部变量的一个典型用法是:希望每一次调用时函数中的变量值能够保存。局部变量在栈区开辟内存,因此函数执行完成后,栈区内存被收回,局部变量也就失效。如果是全局变量呢?变量的值虽然可以保存,但是变量的作用域就不仅仅是函数内部了,也不再受函数的控制,会给程序的维护带来不便。静态局部变量作用域是定义它的函数,同时在全局变量区分配内存,正好完美解决这一问题。
3、 静态函数
在函数的返回类型前加上static关键字,就把函数定义为静态函数。静态函数和普通函数的最主要区别是作用域。静态函数的作用域是定义它的文件,而普通函数的作用域是整个工程,别的文件通过extern 声明这个函数后就可以使用。因此和静态全局变量类似,静态函数名可以防止不同文件中定义同名函数所带来的冲突。
4、static用法小结
通过以上的分析总结,可知static的主要用法或特征有三点:初始0、持久和隐藏。
(1)初始0
主要针对静态全局变量和静态局部变量来说的。如果静态变量没有显式初始化,则系统默认把其初始化为0,且系统仅在声明处对其进行初始化一次。
(2)持久
此项也是主要针对静态全局变量和静态局部变量来说的。不管是静态全局变量还是静态局部变量均是在全局变量内存区开辟内存,因此其作用时间也就是程序的运行时间。变量的值能够持久保存,直至程序运行结束。
(3)隐藏
这也是static的最主要用途。static可以把变量和函数的作用域限制为定义变量的文件内,而其他文件则不可以使用或访问static定义的变量和函数。这样带来的好处是防止不同文件中定义同名变量或函数所带来的冲突。
4、使用static的一个实例
在编写FPGA NIOS II 的串口驱动程序中用到了静态函数。为了保持驱动函数的独立性,防止与其他文件中定义的函数重名,把与串口有关的函数定义为了静态函数。在uart.c中函数声明程序如下:
在这里声明了uart_send_byte、uart_send_string、uart_init、uart_ISR、set_baudrate等函数为静态函数,其作用域也就被限制于uart.c了,在其他文件中就不可以通过extern声明来使用这些函数了。我们可以通过以下的方法来解决其他文件中使用静态函数的问题。比如我们需要在主程序main.c中使用这些函数。
如上图所示,我们在uart.h中定义一个UART_T类型的结构体。结构体成员是UART驱动程序中的关键参数和函数。定义一个名为uart的UART_T类型的结构体变量,并用extern对这个变量进行声明,从而可以在其他文件中使用这个变量。如下图所示在uart.c文件中对结构体变量uart初始化。把uart_send_byte、uart_send_string、uart_init、set_baudrate等函数赋值给结构体中的函数成员。这样就可以通过访问结构体成员函数来调用uart.c中定义的静态变量了。比如:uart.send_string 就是调用uart_send_string函数。