C/C++中static的用法全局变量与局部变量

1.什么是static?

  static 是C/C++中很经常使用的修饰符,它被用来控制变量的存储方式和可见性。windows

1.1static的引入

  咱们知道在函数内部定义的变量,当程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 若是想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义为全局的变量,但定义一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅受此函数控制)。static关键字则能够很好的解决这个问题。数组

另外,在C++中,须要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见时,可将其定义为静态数据。安全

1.2静态数据的存储

  全局(静态)存储区:分为DATA段和BSS段。DATA段(全局初始化区)存放初始化的全局变量和静态变量;BSS段(全局未初始化区)存放未初始化的全局变量和静态变量。程序运行结束时自动释放。其中BBS段在程序执行以前会被系统自动清0,因此未初始化的全局变量和静态变量在程序执行以前已经为0。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是惟一的一次初始化。函数

在C++中static的内部实现机制:静态数据成员要在程序一开始运行时就必须存在。由于函数在程序运行中被调用,因此静态数据成员不能在任何函数内分配空间和初始化。
       这样,它的空间分配有三个可能的地方,一是做为类的外部接口的头文件,那里有类声明;二是类定义的内部实现,那里有类的成员函数定义;三是应用程序的main()函数前的全局数据声明和定义处。
      静态数据成员要实际地分配空间,故不能在类的声明中定义(只能声明数据成员)。类声明只声明一个类的“尺寸和规格”,并不进行实际的内存分配,因此在类声明中写成定义是错误的。它也不能在头文件中类声明的外部定义,由于那会形成在多个使用该类的源文件中,对其重复定义。
      static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间,静态数据成员按定义出现的前后顺序依次初始化,注意静态成员嵌套时,要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。
优点:能够节省内存,由于它是全部对象所公有的,所以,对多个对象来讲,静态数据成员只存储一处,供全部对象共用。静态数据成员的值对每一个对象都是同样,但它的值是能够更新的。只要对静态数据成员的值更新一次,保证全部对象存取更新后的相同的值,这样能够提升时间效率。
this

2.在C/C++中static的做用

2.1总的来讲:

(1)生命周期:在修饰变量的时候,static修饰的静态局部变量只执行初始化一次,并且延长了局部变量的生命周期,直到程序运行结束之后才释放,但不改变做用域。好比修饰函数中存放在栈空间的数组。若是不想让这个数组在函数调用结束释放可使用static修饰。
(2)可见性:static修饰全局变量或函数时,这个全局变量只能在本文件中访问,不能在其它文件中访问,即使是extern外部声明也不能够。这个函数也只能在本文件中调用,不能被其余文件调用。
(3)存储方式:Static修饰的变量存放在全局数据区的静态变量区,包括全局静态变量和局部静态变量,都在全局数据区分配内存。初始化的时候自动初始化为0。
(4)考虑到数据安全性(当程序想要使用全局变量的时候应该先考虑使用static)。
spa

2.2静态变量与普通变量

静态全局变量有如下特色:
(1)静态变量都在全局数据区分配内存,包括后面将要提到的静态局部变量;
(2)未经初始化的静态全局变量会被程序自动初始化为0(在函数体内声明的自动变量的值是随机的,除非它被显式初始化,而在函数体外被声明的自动变量也会被初始化为0);
(3)静态全局变量在声明它的整个文件都是可见的,而在文件以外是不可见的。
优势:静态全局变量不能被其它文件所用;其它文件中能够定义相同名字的变量,不会发生冲突。

(4)全局变量和全局静态变量的区别.net

1)全局变量是不显式用static修饰的全局变量,全局变量默认是有外部连接性的,做用域是整个工程,在一个文件内定义的全局变量,在另外一个文件中,经过extern 全局变量名的声明,就可使用全局变量。
2)全局静态变量是显式用static修饰的全局变量,做用域是声明此变量所在的文件,其余的文件即便用extern声明也不能使用。

2.3静态局部变量有如下特色:

(1)该变量在全局数据区分配内存;
(2)静态局部变量在程序执行到该对象的声明处时被首次初始化,即之后的函数调用再也不进行初始化;
(3)静态局部变量通常在声明处初始化,若是没有显式初始化,会被程序自动初始化为0;
(4)它始终驻留在全局数据区,直到程序运行结束。但其做用域为局部做用域,当定义它的函数或语句块结束时,其做用域随之结束。
  通常程序把新产生的动态数据存放在堆区,函数内部的自动变量存放在栈区。自动变量通常会随着函数的退出而释放空间,静态数据(即便是函数内部的静态局部变量)也存放在全局数据区。全局数据区的数据并不会由于函数的退出而释放空间。
看下面的例子:
 1 //example:  2 #include <stdio.h>  3 #include <stdlib.h>  4 int k1 = 1;  5 int k2;  6 static int k3 = 2;  7 static int k4;  8 int main()  9 { 10 static int m1 = 2, m2; 11 int i = 1; 12 char*p; 13 char str[10] = "hello"; 14 char*q = "hello"; 15 p = (char *)malloc(100); 16 free(p); 17 printf("栈区-变量地址 i:%p\n", &i); 18 printf("栈区-变量地址 p:%p\n", &p); 19 printf("栈区-变量地址 str:%p\n", str); 20 printf("栈区-变量地址 q:%p\n", &q); 21 printf("堆区地址-动态申请:%p\n", p); 22 printf("全局外部有初值 k1:%p\n", &k1); 23 printf(" 外部无初值 k2:%p\n", &k2); 24 printf("静态外部有初值 k3:%p\n", &k3); 25 printf(" 外静无初值 k4:%p\n", &k4); 26 printf(" 内静态有初值 m1:%p\n", &m1); 27 printf(" 内静态无初值 m2:%p\n", &m2); 28 printf(" 文字常量地址:%p, %s\n", q, q); 29 printf(" 程序区地址:%p\n", &main); 30 return 0; 31 }

3.1特别的,在C++中:

static关键字最基本的用法是:线程

一、被static修饰的变量属于类变量,能够经过类名.变量名直接引用,而不须要new出一个类来指针

二、被static修饰的方法属于类方法,能够经过类名.方法名直接引用,而不须要new出一个类来code

被static修饰的变量、被static修饰的方法统一属于类的静态资源,是类实例之间共享的,换言之,一处变、到处变。

  在C++中,静态成员是属于整个类的而不是某个对象,静态成员变量只存储一份供全部对象共用。因此在全部对象中均可以共享它。使用静态成员变量实现多个对象之间的数据共享不会破坏隐藏的原则,保证了安全性还能够节省内存。

静态成员的定义或声明要加个关键static。静态成员能够经过双冒号来使用即<类名>::<静态成员名>。

3.2静态类相关

 1 example1:经过类名调用静态成员函数和非静态成员函数  2 class Point  3  {  4 public:  5 void init()  6  {  7  }  8 static void output()  9  { 10  } 11  }; 12 void main() 13  { 14  Point::init(); 15  Point::output(); 16 } 

报错: 'Point::init' : illegal call of non-static member function

结论1:不能经过类名来调用类的非静态成员函数。

 1 //example2:经过类的对象调用静态成员函数和非静态成员函数  2 class Point  3  {  4 public:  5 void init()  6  {  7  }  8 static void output()  9  { 10  } 11  }; 12 void main() 13 { 14  Point pt; 15  pt.init(); 16  pt.output(); 17 } 

编译经过。

结论2:类的对象可使用静态成员函数和非静态成员函数。

 1 //example3:在类的静态成员函数中使用类的非静态成员  2 #include <stdio.h>  3 class Point  4 {  5 public:  6 void init()  7  {  8  }  9 static void output() 10  { 11 printf("%d\n", m_x); 12  } 13 private: 14 int m_x; 15 }; 16 void main() 17 { 18  Point pt; 19  pt.output(); 20 } 

编译出错:error C2597: illegal reference to data member 'Point::m_x' in a static member function

  由于静态成员函数属于整个类,在类实例化对象以前就已经分配空间了,而类的非静态成员必须在类实例化对象后才有内存空间,因此这个调用就出错了,就比如没有声明一个变量却提早使用它同样。

结论3:静态成员函数中不能引用非静态成员。

 1 //example4:在类的非静态成员函数中使用类的静态成员  2 class Point  3 {  4 public:  5 void init()  6  {  7  output();  8  }  9 static void output() 10  { 11  } 12 }; 13 void main() 14 { 15  Point pt; 
15 Pt.init(); 16 pt.output(); 17 }

编译经过。

结论4:类的非静态成员函数能够调用用静态成员函数,但反之不能。

 1    //example5:使用类的静态成员变量  2 #include <stdio.h>  3 class Point  4  {  5 public:  6  Point()  7  {  8 m_nPointCount++;  9  } 10 ~Point() 11  { 12 m_nPointCount--; 13  } 14 static void output() 15  { 16 printf("%d\n", m_nPointCount); 17  } 18 private: 19 static int m_nPointCount; 20  }; 21 void main() 22  { 23  Point pt; 24  pt.output(); 25 } 

按Ctrl+F7编译无错误,按F7生成EXE程序时报连接错误

error LNK2001: unresolved external symbol "private: static int Point::m_nPointCount" (?m_nPointCount@Point@@0HA)

这是由于类的静态成员变量在使用前必须先初始化。

在main()函数前加上int Point::m_nPointCount = 0;

再编译连接无错误,运行程序将输出1。

结论5:类的静态成员变量必须先初始化再使用。

  思考总结:静态资源属于类,可是是独立于类存在的。从J类的加载机制的角度讲,静态资源是类初始化的时候加载的,而非静态资源是类实例化对象的时候加载的。 类的初始化早于类实例化对象,好比Class.forName(“xxx”)方法,就是初始化了一个类,可是并无实例化对象,只是加载这个类的静态资源罢 了。因此对于静态资源来讲,它是不可能知道一个类中有哪些非静态资源的;可是对于非静态资源来讲就不同了,因为它是实例化对象出来以后产生的,所以属于类的这些东西它都能认识。因此上面的几个问题答案就很明确了:

1)静态方法能不能引用非静态资源?不能,实例化对象的时候才会产生的东西,对于初始化后就存在的静态资源来讲,根本不认识它。

2)静态方法里面能不能引用静态资源?能够,由于都是类初始化的时候加载的,你们相互都认识。

3)非静态方法里面能不能引用静态资源?能够,非静态方法就是实例方法,那是实例化对象以后才产生的,那么属于类的内容它都认识。

  (static修饰类这个用得相对比前面的用法少多了,static通常状况下来讲是不能够修饰类的, 若是static要修饰一个类,说明这个类是一个静态内部类(注意static只能修饰一个内部类),也就是匿名内部类。像线程池 ThreadPoolExecutor中的四种拒绝机制CallerRunsPolicy、AbortPolicy、DiscardPolicy、 DiscardOldestPolicy就是静态内部类。静态内部类相关内容会在写内部类的时候专门讲到。)

3.3总结:

(1)静态成员函数中不能调用非静态成员。

(2)非静态成员函数中能够调用静态成员。由于静态成员属于类自己,在类的对象产生以前就已经存在了,因此在非静态成员函数中是能够调用静态成员的。

(3)静态成员变量使用前必须先初始化(如int MyClass::m_nNumber = 0;),不然会在linker时出错。

参考:http://blog.csdn.net/morewindows/article/details/6721430

 通常总结:在类中,static能够用来修饰静态数据成员和静态成员方法
静态数据成员
(1)静态数据成员能够实现多个对象之间的数据共享,它是类的全部对象的共享成员,它在内存中只占一份空间,若是改变它的值,则各对象中这个数据成员的值都被改变。
(2)静态数据成员是在程序开始运行时被分配空间,到程序结束以后才释放,只要类中指定了静态数据成员,即便不定义对象,也会为静态数据成员分配空间。
(3)静态数据成员能够被初始化,可是只能在类体外进行初始化,若未对静态数据成员赋初值,则编译器会自动为其初始化为0
(4)静态数据成员既能够经过对象名引用,也能够经过类名引用。

静态成员函数 (1)静态成员函数和静态数据成员同样,他们都属于类的静态成员,而不是对象成员。 (2)非静态成员函数有this指针,而静态成员函数没有this指针。 (3)静态成员函数主要用来方位静态数据成员而不能访问非静态成员。

相关文章
相关标签/搜索