C语言中的extern,static和register的一些用法_在c语言程序中可以用 extern register-程序员宅基地

技术标签: C  static  c语言  extern  register  

在谈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使得它所引用的是全局变量。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/xy913741894/article/details/52213297

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include&lt;stdio.h&gt;#include&lt;string.h&gt;#include&lt;stdlib.h&gt;#include&lt;malloc.h&gt;#include&lt;iostream&gt;#include&lt;stack&gt;#include&lt;queue&gt;using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签