http://baike.baidu.com/link?url=Ghi98o0--21o9KXmNyqaqE4NibtYQOJ2WwexDRAUCzXAEn6jp3qX_IjHzA9U3gs_
一、面向过程
静态全局变量
注意:全局变量和全局静态变量的区别
1)全局变量是不显式用static修饰的全局变量,但全局变量默认是动态的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern全局变量名的声明,就可以使用全局变量。
2)全局静态变量是显式用static修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用extern声明也不能使用。
静态局部变量
在局部变量前,加上关键字static,该变量就被定义成为一个静态局部变量。
静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。
静态局部变量有以下特点:
该变量在全局数据区分配内存;
静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;
静态函数
在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。
定义静态函数的好处:
静态函数不能被其它文件所用;
其它文件中可以定义相同名字的函数,不会发生冲突;
二、面向对象
静态数据成员
在类内数据成员的声明前加上关键字static,该数据成员就是类内的静态数据成员。
静态数据成员有以下特点:
对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当作是类的成员。无论这个类的对象被定义了多少个,静态数据成员在程序中也只有一份拷 贝,由该类型的所有对象共享访问。也就是说,静态数据成员是该类的所有对象所共有的。对该类的多个对象来说,静态数据成员只分配一次内存,供所有对象共 用。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新;
静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义。
静态成员函数
与静态数据成员一样,我们也可以创建一个静态成员函数,它为类的全部服务而不是为某一个类的具体对象服务。静态成员函数与静态数据成员一样,都是类的内部 实现,属于类定义的一部分。
关于静态成员函数,可以总结为以下几点:
出现在类体外的函数定义不能指定关键字static;
静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;
非静态成员函数可以任意地访问静态成员函数和静态数据成员;
静态成员函数不能访问非静态成员函数和非静态数据成员;
由于没有this指针的额外开销,因此静态成员函数与类的全局函数相比速度上会有少许的增长;
作用
static静态变量声明符。在声明它的程序块,子程序块或函数内部有效,值保持,在整个程序期间分配存储器空间,编译器默认值0。
为什么要引入static
函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,大家知道,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义一个全局的变量,但定义为一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅受此函数控制)。
什么时候用static
需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见。
优势
可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。
注意事项
⑴类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致了它仅能访问类的静态数据和静态成员函数。
⑵不能将静态成员函数定义为虚函数。
⑶由于静态成员声明于类中,操作于其外,所以对其取地址操作,就多少有些特殊,变量地址是指向其数据类型的指针 ,函数地址类型是一个“nonmember函数指针”。
⑷由于静态成员函数没有this指针,所以就差不多等同于nonmember函数,结果就产生了一个意想不到的好处:成为一个callback函数,使得我们得以将C++和C-based X Window系统结合,同时也成功的应用于线程函数身上。
⑸static并没有增加程序的时空开销,相反她还缩短了子类对父类静态成员的访问时间,节省了子类的内存空间。
⑹静态数据成员在<;定义或说明>;时前面加关键字static。
⑺静态数据成员是静态存储的,所以必须对它进行初始化。
⑻静态成员初始化与一般数据成员初始化不同:
初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆;
初始化时不加该成员的访问权限控制符private,public等;
初始化时使用作用域运算符来标明它所属类;
所以我们得出静态数据成员初始化的格式:<;数据类型><;类名>::<;静态数据成员名>=<;值>
⑼为了防止父类的影响,可以在子类定义一个与父类相同的静态变量,以屏蔽父类的影响。这里有一点需要注意:我们说静态成员为父类和子类共享,但我们有重复定义了静态成员,这会不会引起错误呢?不会,我们的编译器采用了一种绝妙的手法:name-mangling 用以生成唯一的标志。在各通信公司的笔试面试中经常出现的考题就是static的作用及功能。
C中变量定义的三个修饰符
变量定义有三个修饰符值得注意,虽然它们与标准C是相同的,但是在嵌入式C语言中又有不同的含义。
1) static
在子函数中static用声明的变量是局部变量,但是退出这个子函数后其值不消失。下一次调用这个函数时仍可以访问到原来的值。注意,在子函数中声明的static变量只对声明他的函数可见,别的函数是不可以使用的。如果static变量是在模块中声明的,那么只有本模块的函数可以使用它,别的模块中的函数是不能访问的。
void MyFunction (void)
{
static char myVar = 0; //用 static声明的局部变量
myVar = myVar + 1;
}
void main (void)
{
MyFunction(); // 调用之前myVar = 0,调用之后myVar = 1
MyFunction(); // 调用之前myVar = 1,调用之后myVar = 2
}
2) volatile
如果一个变量的值可能会被程序操作之外的其它操作所改变,那么你必需用volatile 声明。在嵌入式系统中其它操作是:中断服务程序的操作、硬件动作的操作。
用volatile声明的变量是不会被编译器优化掉的,如:
volatile unsigned char PortA @0x0000;
PORTA做为一个输入端口,其值是由外部设备决定的,由于外部设备的变化是随机的,因此第一次读取的值和第二次读取的值很可能不同,所以我们把它声明为volatile变量。
a = PORTA;
a = PORTA;
由于PORTA是用volatile声明的变量,编译器不会把它优化成一句,而如果不是volatile声明的编译器就会将第二句优化掉,从而程序将会忽略输入端口的变化。
通常把嵌入式设备的所有外围器件寄存器都声明为volatile 的。
3) const
修饰符 const 可以用在任何变量之前, 告诉编译器把此变量存储在ROM中。ROM_VAR段是定位 const 变量的默认段
语法格式:#pragma CONST_SEG <段名>
例如:
#pragma DATA_SEG DEFAULT
#pragma CONST_SEG DEFAULT
static int a; // 变量 a 存放在默认的 RAM 段 DEFAULT_RAM 中,DEFAULT_RAM是段名
static const int c0 = 10; // 变量 c0 存放在默认的 ROM 段 ROM_VAR 中,ROM_VAR是段名
此时编译器选项-Cc必需是打开的。如果编译器选项-Cc必需是关闭的,则变量a和c0都定位在DEFAULT_RAM中。
例如:
#pragma DATA_SEG MyVarSeg
#pragma CONST_SEG MyConstSeg
static int a; // 变量 a 存放在段MyVarSeg中,MyVarSeg是段名
static const int c0 = 10; // 变量 c0 存放在段 MyConstSeg 中,MyConstSeg是段名
此时编译器选项-Cc必需是打开的。如果编译器选项-Cc必需是关闭的,则变量a和c0都定位在MyVarSeg中
static:修饰符:修饰变量,函数。作用域:变量仅仅在本文件可见,函数在本文件可以被调用。static在函数内部定义的话,分配在堆中,数值保存在data段,而不是在栈中,而且只赋值一次。
extern:修饰符:修饰变量,函数。修饰变量时候,变量的声明在外面,修饰函数的时候,如果函数已经声明,则作用不是很大。
const:修饰符:修饰变量,函数。修饰变量时候,不能被重复赋值,只能放在只读段中。修饰函数时候,表明函数的返回值必须为常数。
volatile:(嵌入式程序员必须掌握的)volatile最初的意思是表示汽油容易挥发,在c中的作用大概有两点
(1)表示变量是易失的,易变的。
(2)强制访存操作,防止编译器去优化,告诉编译器每次必须去内存中取值,而不是从寄存器或者缓存。