一,全局变量和局部变量概念

1,作用域

能使用某个变量的所有语句叫做变量的作用域。

声明在函数内部的变量叫局部变量,局部变量的作用域是它所在函数内部的所有语句,声明在所有函数外边的变量叫全局变量,它们的作用域是程序中的所有语句。

2,全局变量

全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化。普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。

3,局部变量

静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。在定义不需要与其他文件共享的全局变量时,加上static关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用。

二,static三种不同的用法:1.修饰函数,2.修饰局部变量,3.修饰全局变量。

1,static修饰函数:static函数与普通函数的区别

(1)用static修饰的函数,限定在本源码文件中使用,不能被本源码文件以外的代码文件调用。

(2)普通的函数,默认是extern的,也就是说,可以被其它代码文件调用该函数。

若在函数的返回类型前加上关键字static,函数就被定义成为静态函数。普通函数的定义和声明默认情况下是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。因此定义静态函数有以下好处:

其他文件中可以定义相同名字的函数,不会发生冲突。

静态函数不能被其他文件所用。

void fn(void){int n = 10; printf("n=%d\n", n);n++;printf("n++=%d\n", n);} void fn_static(void){static int n = 10; printf("static n=%d\n", n);n++;printf("n++=%d\n", n);} int main(void){fn();printf("--------------------\n");fn_static();printf("--------------------\n");fn();printf("--------------------\n");fn_static();return 0;}

2,static修饰:被static修饰的局部变量被称做静态变量。

因为static修饰的变量在一个程序只会执行一次,之后就不会发生改变了。

本质上是因为static修饰的局部变量存储位置改变了,局部变量是存储在栈区内,而静态变量则是存储在静态区内。而这种改变带来的影响就是使得局部变量的生命周期变得和整个程序一样长,即:局部变量出了其作用域也将不会销毁,除非程序结束。值得注意的是虽然局部变量的声明周期得到了很大的提升,但他的作用域没有发生任何的改变,还是只能在那个局部的范围内使用。

#include//局部变量的生命周期就是其所在的那个局部范围,//进这个范围变量就创建,出这个范围变量就销毁。//每次调用函数test,局部变量a就创建,//调用结束就把a销毁,所以每次打印会是2。void test(){int a = 1;a++;printf("%d ", a);}void test_static(){static int a = 1;a++;printf("%d ", a);}int main(){int i = 0;while (i < 10){test();i++;}while (i < 10){test_static();i++;}return 0;}

运行结果:

2 2 2 2 2 2 2 2 2 2

2 3 4 5 6 7 8 9 10 11

3,static修饰全局变量

fun.c中定义:static int m=5;

main.c中定义:extern int m; printf(“m=%d\n”,m);

会报错的,全局变量是具有外部链接属性的,而static修饰的全局变量的时候就把这个外部链接属性变成了内部链接属性。这就导致其他源文件不能使用该全局变量了。所以你才会感觉似乎全局变量的作用域变小了,但生命周期不变,仍是整个工程。

总结:没有初始化的全局变量会自动被初始化成0。全局变量和局部变量可以重名,这种时候变量名优先代表局部变量。如果全局变量和局部变量都可以解决问题就应该优先选择使用局部变量;一个存储区只能在某个时间范围内才可以使用,这个时间范围叫做存储区的生命周期。全局变量存储区的生命周期是整个程序的执行时间范围;局部变量存储区的生命周期是函数某一次执行的时间范围;一个函数不可以使用另外函数的变量,但是有可能可以使用那个函数的存储区;使用数组作为形式参数可以实现跨函数使用存储区;

三,extern

1,extern函数:

(1)永远把extern变量和函数声明在头文件中,不要把它们放在源文件里,保持一处声明原则;(2)不要忘记在定义extern变量和函数处include它们的头文件

(3)不要忘记在使用extern变量和函数处include它们的头文件

(4)不要忘记头文件全局变量之前的extern关键字,否则多个源文件include该头文件后,会多次创建这个变量。

//全局函数声明处 globalfun.hextern int incrementGlobal(void);//全局函数定义处#include "globalFun.h"int incrementGlobal(void) {return ++global_variable;}//全局函数的使用处 funuse.c#include "globalfun.h"printf("Increment Global: %d\n", incrementGlobal());

2,extern变量

//全局变量声明处globalar.hextern int global_variable;//全局变量定义处#include "globalar.h"int global_variable = 0; // 变量定义要放在函数外,是全局范围的定义//全局变量使用处use.c#include "globalVar.h"printf("Global Variable: %d\n", global_variable);

四,volatile用法

1,解释

volatile是一个特征修饰符(type specifier).volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。

2,一个共享变量(类的成员变量、类的静态成员变量)被 volatile 修饰之后:保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。禁止进行指令重排序。

3,应用场景:对变量的写操作不依赖于当前值。该变量没有包含在具有其他变量的不变式中。

举例1:

dma.c中定义 volatile int Rx_Done; Rx_Done=0;

main.c中无需定义,只需包含#include”dma.h” Rx_Done=1;

或者

timer.c中 volatile u8 usec; usec++;

main.c中 无需定义只需包含。 use=3;

举例2:

soft.c中 extern u8 flag; flag=1;(条件触发)

main.c中中无需定义,只需包含#include”soft.h”

volatile u8 flag=0; if(flag==1) flag=0;