您的位置:首页 > 编程语言 > C语言/C++

C语言中的extern,static和register的一些用法

2016-08-15 17:49 459 查看
在谈extern和static用法之前,我们首先要清楚了解一些基础概念。
linkage链接属性有三种:external, internal, none。C和指针给出的介绍如下:
Identifiers that  have no linkage are always individuals, that is, multiple declarations of the identifier  are always treated as separate and distinct entities.
Internal linkage means that all  declarations  of  the  identifier  within  one  source  file  refer  to  a  single  entity,  but  declarations  of  the  same  identifier  in  other  source files  refer to  different  entities. 
Finally, all references to an identifier with external linkage refer to the same entity. 
我们通过下面的代码片段来看看链接属性是怎么回事?
int b;
int c (int d)
{
     int e;
     int f (int g);
     ......
}
标识符b,c,f的链接属性为external,剩下的诸如d,e,g都是none。
记住一点函数名和定义在函数外面的默认链接属性为external,这意味着假如这段代码在a.c这个文件中,另一个文件的b.c能够访问到b,c,f。

而如果仅仅想让b,c,f只在当前文件a.c使用,不想被其他文件访问,这个时候我们的external和static就起作用了:The keywords extern and static are used in declarations to modify the linkage  of the identifiers being declared.
比如static int b;关键词static就将b的属性改为internal,这意味着其他文件不能访问到该文件a.c中的b了。
同理static int c (int d);static int f (int g);也是如此。
但是需要注意一点:static only has this effect in declarations whose default linkage is external.   

而extern用法更复杂一点, 在C和指针中这么描述它:it specifies  external linkage for an identifier and is used to get access to an entity that is defined  elsewhere.我们通过下面的代码来解释这句话
static int i;
int fun ()
{
     int j;
     extern int k;
     extern int i;

}
在声明中将k指定为external链接属性,这使得fun函数能够访问到声明在其他源文件的中变量k。
另外一点是函数里面的exern int i并不能改变第一次定义时候 (static int i) 的链接属性。
When extern is used on the first declaration in the source file for an identifier, it  specifies  that  the  identifier  has  external  linkage.  When  used  on  the  second  or  subsequent declarations of an identifier, however, the keyword does not change the 
linkage specified by the first declaration. 

下面我们再来看看存储类型(存储地址),书上给出的定义是The storage class of a variable refers to the type of memory in which the variable’s  value is stored. The storage class of a variable determines when it is created and  destroyed and how long it will retain its value. There are three possible places to store 
variables:  in  ordinary  memory,  on  the  runtime  stack,  and  in  hardware  registers.

 一个变量默认的存储类型是由它声明的地方决定。
Variables declared outside of any blocks are always stored in static memory, that is, in  memory that is not part of the stack. There is no way to specify any other storage class  for these variables. Static variables are created before the program begins to run and 
exist throughout its entire execution. They retain whatever value they were assigned  until a different value is assigned or until the program completes. 
The default storage class for variables declared within a block is automatic, that  is, on the stack. There is a keyword auto, but it is rarely used because it doesn’t change  the default. Automatic variables are created just before the program execution enters 
the block in which they were declared, and they are discarded just as execution leaves  that block. If the block is executed several times, as in the case of a function that is  called repeatedly, new copies of the automatic variables are created each time.
We therefore say  that automatic variables disappear at the end of a block; they generally will not have  their previous values the next time the block is entered.

但是如果变量声明在block内的话,用static可以改变它的存储类型,由auto变为static。此时这个变量的生存周期就变为整个程序,而非仅在它所声明的语句块内。但是改变存储类型并不改变它的作用域 ,只有在块内才可以访问它。我们通过一段代码来理解它。
#include <stdio.h>
void fun()
{
    static int times = 0;
    times++;
    printf("函数执行次数为%d\n", times);   
}

int main()
{
    int i = 0;
    for (i = 0; i < 5; i++)
        fun();
    //printf("times=%d\n", times);不能使用times([Error] 'times' undeclared (first use in this function))
    return 0;     
}
结果是:
函数执行次数为1
函数执行次数为2
函数执行次数为3
函数执行次数为4
函数执行次数为5
注意一点:函数的形参不能声明为static因为参数要在栈上传递来支持递归(recursion).

最后我们来看看register这个关键词,书上给出的定义:the  register  keyword  may  be  used  on  declarations  of  automatic  variables to indicate that they should be stored in the machine’s hardware registers  rather than in memory.Register variables are created and destroyed at the same time as automatic 
variables, but there is some additional work needed. 
为什么要有register变量呢?主要是寄存器比内存访问速度快,更加有效。然而寄存器个数是有限的,因此编译器可以忽视这个关键词,这意味着如果有很多变量声明为register只有之前的一部分会放到寄存器中。因此你可以选择一些经常被使用的变量放在寄存器中来提高效率。
在一个使用寄存器变量的函数返回前,这些寄存器之前的存储的值必须恢复,确保调用者的寄存器变量未被破坏,许多机器使用运行时候堆栈来完成这个任务。当函数开始执行时候,它把需要使用的所有寄存器内存都保存在堆栈,当函数返回时候,这些纸在复制回寄存器中。在许多机器的硬件实现中,并不为寄存器指定地址。

总结:
static用于函数定义时,或者代码块之外的变量声明,static用于修改标识符的链接属性从external到internal,但标识符的存储类型和作用域不受影响,用这种方式声明的函数或变量只能在声明它们的源文件中访问。

static用于块内的变量声明,它用于修改变量的存储类型,从auto变为static,但链接属性和作用域不受影响。用这种方式声明的变量在程序执行前创建,并在整个程序执行期间一直存在,而非执行时创建,执行完毕销毁。

具有external链接属性的实体称为全局实体(global)所有源文件的所有函数都可以访问,只要变量并非声明于代码块内或函数定义内,它默认为external。当变量声明于代码块内,在它前面加extern使得它所引用的是全局变量。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: