c/c++ static关键字详解

    静态变量作用范围在一个文件内,程序开始时分配空间,结束时释放空间,默认初始化为0,使用时可以改变其值。

    静态变量或静态函数只有本文件内的代码才能访问它,它的名字在其它文件中不可见。
用法1:函数内部声明的static变量,可作为对象间的一种通信机制
    如果一局部变量被声明为static,那么将只有唯一的一个静态分配的对象,它被用于在该函数的所有调用中表示这个变量。这个对象将只在执行线程第一次到达它的定义使初始化。
用法2:局部静态对象
    对于局部静态对象,构造函数是在控制线程第一次通过该对象的定义时调用。在程序结束时,局部静态对象的析构函数将按照他们被构造的相反顺序逐一调用,没有规定确切时间。
 用法3:静态成员和静态成员函数
  如果一个变量是类的一部分,但却不是该类的各个对象的一部分,它就被成为是一个static静态成员。一个static成员只有唯一的一份副本,而不像常规的非static成员那样在每个对象里各有一份副本。同理,一个需要访问类成员,而不需要针对特定对象去调用的函数,也被称为一个static成员函数。
类的静态成员函数只能访问类的静态成员(变量或函数)。

    进一步详细解释如下:

1.先来介绍它的第一条也是最重要的一条:隐藏

     当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。为理解这句话,我举例来说明。我们要同时编译两个源文件,一个是a.c,另一个是main.c. 下面是a.c的内容:

char a = 'A'; // global variable

void msg() { printf("Hello\n"); }

    下面是main.c的内容:

int main(void) { 

    extern char a; // extern variable must be declared before use

    printf("%c ", a);

    (void)msg();

    return 0;  }

    程序的运行结果是:

A Hello

    你可能会问:为什么在a.c中定义的全局变量a和函数msg能在main.c中使用?前面说过,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。

如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。Static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用。

2. static的第二个作用是保持变量内容的持久

    存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。虽然这种用法不常见,但我还是举一个例子。

#include <stdio.h>

 int fun(void){

static int count = 10; // 事实上此赋值语句从来没有执行过

 return count--;

 }

 int count = 1;

int main(void) {

printf("global\t\tlocal static\n");

 for(; count <= 10; ++count)

    printf("%d\t\t%d\n", count, fun());

 return 0; }

    程序的运行结果是:

global local static
1  10
2   9
3   8
4   7
5   6
6   5
7   4
8   3
9   2
10  1

3. static的第三个作用是默认初始化为0.其实全局变量也具备这一属性,因为全局变量也存储在静态数据区

    在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加‘\0’太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是‘\0’。不妨做个小实验验证一下。

#include <stdio.h>

 int a;

int main(void){

int i;

static char str[10];

printf("integer: %d; string: (begin)%s(end)", a, str);

 return 0;

 }

    程序的运行结果如下integer: 0; string: (begin)(end)

    最后对static的三条作用做一句话总结。首先static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性和默认值0.

4. 用static声明的函数和变量小结

    static 声明的变量在C语言中有两方面的特征:

1)、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。

2)、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。

 Tips:
A.若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
B.若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
C.设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题;
D.如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static变量(这样的函数被称为:带“内部存储器”功能的的函数)
E.函数中必须要使用static变量情况:比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。

    函数前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。

扩展分析:

     术语static有着不寻常的历史.起初,在C中引入关键字static是为了表示退出一个块后仍然存在的局部变量。随后,static在C中有了第二种含义:用来表示不能被其它文件访问的全局变量和函数。为了避免引入新的关键字,所以仍使用static关键字来表示这第二种含义。最后,C++重用了这个关键字,并赋予它与前面不同的第三种含义:表示属于一个类而不是属于此类的任何特定对象的变量和函数(与Java中此关键字的含义相同)。

全局变量、静态全局变量、静态局部变量和局部变量的区别

变量可以分为:全局变量、静态全局变量、静态局部变量和局部变量。

(1) 按存储区域分,全局变量、静态全局变量和静态局部变量都存放在内存的静态存储区域,局部变量存放在内存的栈区。

(2) 按作用域分,  全局变量在整个工程文件内都有效;静态全局变量只在定义它的文件内有效;静态局部变量只在定义它的函数内有效,只是程序仅分配一次内存,函数返回后,该变量不会消失;局部变量在定义它的函数内有效,但是函数返回后失效。

     全局变量(外部变量)的说明之前再冠以static就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。

    从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。

(1) static 函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件

(2) static全局变量与普通的全局变量有什么区别:static全局变量只初始化一次,防止在其他文件单元中被引用;

(3) static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
(4) static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝.
(5) 全局变量和静态变量如果没有手工初始化,则由编译器初始化为0。局部变量的值不可知。

5. C++的static

    C++的static有两种用法:面向过程程序设计的static和面向对象程序设计中的static。前者应用于普通变量和函数,不涉及类;后者主要说明static在类中的作用。

(1)、面向过程设计中的static

1)、静态全局变量

在全局变量前,加上关键字static,该变量就被定义成为一个静态全局变量。我们先举一个静态全局变量的例子,如下: 

//Example 1

#include <iostream.h>

void fn();

static int n; //定义静态全局变量

void main()

{

 n=20;

 cout<<n<<endl;

 fn();

}

void fn()

{

 n++;

 cout<<n<<endl;

}

    静态全局变量有以下特点:  

 i )  该变量在全局数据区分配内存;
  ii )  未经初始化的静态全局变量会被程序自动初始化为0(自动变量的值是随机的,除非它被显式初始化);   
  iii ) 静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的;

    静态变量都在全局数据区分配内存,包括后面将要提到的静态局部变量。对于一个完整的程序,在内存中的分布情况如下图:

代码区

全局数据区

堆区

栈区

  一般程序的由new产生的动态数据存放在堆区,函数内部的自动变量存放在栈区。自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静态局部变量)也存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。细心的读者可能会发现,Example 1中的代码中将 

 static int n; //定义静态全局变量

改为 

 int n; //定义全局变量

    程序照样正常运行。的确,定义全局变量就可以实现变量在文件中的共享,但定义静态全局变量还有以下好处: 

1) 静态全局变量不能被其它文件所用;  

2) 其它文件中可以定义相同名字的变量,不会发生冲突; 

您可以将上述示例代码改为如下:

//Example 2

//File1

#include <iostream.h>

void fn();

static int n; //定义静态全局变量

void main()

{

 n=20;

 cout<<n<<endl;

 fn();

}

//File2

#include <iostream.h>

extern int n;

void fn()

{

 n++;

 cout<<n<<endl;

}

    编译并运行Example 2,您就会发现上述代码可以分别通过编译,但运行时出现错误。试着将  

static int n; //定义静态全局变量

改为  

int n; //定义全局变量

    再次编译运行程序,细心体会全局变量和静态全局变量的区别。  

(2)、静态局部变量

     在局部变量前,加上关键字static,该变量就被定义成为一个静态局部变量。 我们先举一个静态局部变量的例子,如下: 

//Example 3

#include <iostream.h>

void fn();

void main()

{

 fn();

 fn();

 fn();

}

void fn()

{

 static n=10;

 cout<<n<<endl;

 n++;

}

  通常,在函数体内定义了一个变量,每当程序运行到该语句时都会给该局部变量分配栈内存。但随着程序退出函数体,系统就会收回栈内存,局部变量也相应失效。但是有时候我们需要在两次调用之间对变量的值进行保存。通常的想法是定义一个全局变量来实现。但这样一来,变量已经不再属于函数本身了,不再仅受函数的控制,给程序的维护带来不便。  静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下

时间: 2024-10-18 20:23:13

c/c++ static关键字详解的相关文章

PHP5中的this,self和parent关键字详解

php5|关键字|详解 PHP5是一具备了大部分面向对象语言的特性的语言,比PHP4有了很多的面向对象的特性,但是有部分概念也比较绕人,所以今天拿出来说说,说的不好,请高手见谅. (阅读本文,需要了解PHP5的面向对象的知识) 首先我们来明白上面三个关键字: this,self,parent,从字面上比较好理解,是指这,自己,父亲,呵呵,比较好玩了,我们先建立几个概念,这三个关键字分别是用在什么地方呢?我们初步解释一下,this是指向当前对象的指针(我们姑且用C里面的指针来看吧),self是指向

java关键字(详解)

基本类型 1 boolean 布尔型 2 byte 字节型 3 char 字符型 4 double 双精度 5 float 浮点 6 int 整型 7 long 长整型 8 short 短整型 9 null 空 10 true 真 11 false 假 程序控制语句 1 break 跳出中断 2 continue 继续 3 return 返回 4 do 运行 5 while 循环 6 if 如果 7 else 否则 8 for 循环 9 instanceof 实例 10 switch 观察 11

Java中final关键字详解_php技巧

谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. 主要介绍:一.final关键字的基本用法.二.深入理解final关键字 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量).下面就从这三个方面来了解一下final关键字的基本用法. 1.修饰类 当用final修饰一个类时,表明这个类不能

java多线程编程之Synchronized关键字详解_java

本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章  一.分析 synchronized可以修饰实例方法,如下形式: public class MyObject { synchronized public void methodA() { //do something.... } 这里,synchronized 关键字锁住的是当前对象.这也是称为对象锁的原因. 为啥锁住当

Java 多线程synchronized关键字详解(六)_java

synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C. D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C .D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行.它包括两种用法:synchronized 方法和 synchronized 块. 多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同步用以解决多

Java transient 关键字详解及实例代码_java

Java transient 关键字 1. transient的作用及使用方法 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化. 然而在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见

Delphi 关键字详解

  Delphi 关键字详解[整理于 "橙子" 的帖子]   absolute //它使得你能够创建一个新变量, 并且该变量的起始地址与另一个变量相同. var Str: string[32]; StrLen: Byte absolute Str; //这个声明指定了变量StrLen起始地址与Str相同. //由于字符串的第0个位置保存了字符串的长度, 所以StrLen的值即字符串长度. begin Str := 'abc'; Edit1.Text := IntToStr(StrLen

javascript中this关键字详解_javascript技巧

不管学习什么知识,习惯于把自己所学习的知识列成一个list,会有助于我们理清思路,是一个很好的学习方法.强烈推荐. 以下篇幅有点长,希望读者耐心阅读. 以下内容会分为如下部分: 1.涵义 1.1:this涵义 1.2:this指向的可变性 2.使用场合 2.1:全局环境 2.2:构造函数 2.3:对象的方法 3.使用注意点 3.1:避免多层嵌套this 3.2:避免数组处理方法中的this 3.3:避免回调函数中的this 1.涵义 1.1:this涵义 在我写的一篇关于 构造函数与new关键字

C++ 中try finally关键字详解_C 语言

try-finally语句是Microsoft对C和C++语言的扩展,它能使32位的目标程序在异常出现时,有效保证一些资源能够被及时清除,这些资源的清除任务可以包括例如内存的释放,文件的关闭,文件句柄的释放等等.try-finally语句特别适合这样的情况下使用,例如一个例程(函数)中,有几个地方需要检测一个错误,并且在错误出现时,函数可能提前返回. #include <windows.h> #include <stdio.h> try-finally语句的语法与try-excep