全局变量/局部变量/静态变量

2023-11-03

1. 全局变量

   全局变量是在所有函数体的外部定义的,程序的所有部分(其他文件的代码)都可以使用。全局变量不受作用域的影响。全局变量的生命周期一直到程序的结束,全局变量是静态存储方式。如果在一个文件中使用extern关键字来声明另一个文件中存在的全局变量,那么这个文件可以使用这个变量。通常都是在一个.CPP文件的开始声明这个变量,在其他需要用到这个变量的地方其头文件也就是.h文件中extern
  type vari;
 
2.  局部变量
  局部变量出现在一个作用域内,它们是局限于一个函数的。局部变量经常被称为自动变量,因为它们在进入作用域时,自动生成,离开作用域时自动消失。关键字auto可以显示的说明这个问题,但局部变量默认为auto,所以没必要声明为auto。通常函数里定义的变量,函数的参数都局部变量。所以一般需要在某个函数里,返回数组的地址或者指针之类,这时候必须定义成static,或者动态申请内存,这样函数结束返回时才不会释放掉那块内存空间。
寄存器变量也是一种局部变量。register来修饰,就是告诉编译器尽可能快的访问这个变量,通过存放在寄存器中来实现快速访问。
 
3.  静态变量
    静态变量的修饰关键字是static,static可以作用于变量以及函数。由static修饰的,可分为静态局部变量,静态全局变量,静态函数。静态变量的生命期和程序生命期是一样的,在程序结束之后操作系统会回收空间。 静态变量当然是属于静态存储方式,但是属于静态存储方式的量不一定就是静态变量。 例如外部变量虽属于静态存储方式,但不一定是静态变量,必须由 static加以定义后才能成为静态外部变量,或称静态全局变量。
!C++中可以在类的数据成员或成员函数之前加上static,这样定义的数据成员或成员函数就被类所拥有,而不再属于类的对象。如下:

<span style="font-size:14px;">class A {  char c;  int a;  static int b; }a ; //X86机器下,sizeof(a)=8 (字节对齐是4的倍数,静态成员变量不算)</span>
 
<1>静态局部变量
 
在局部变量前面加上static后,就定义了静态局部变量,静态局部变量属于静态存储方式,静态局部变量只会被初始化一次,下次使用依据上一次保存的值。它具有以下特点:
(1)  静态局部变量在函数内定义 ,它的生存期为整个源程序,但是其作用域仍与自动变量相同,只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。(作用域不变,生存周期变了)
(2)  允许对构造类静态局部量赋初值 ,例如数组,若未赋以初值,则由系统自动赋以0值。(而非静态局部变量未赋初始值,系统会给个随机值)
(3)  对基本类型的静态局部变量,若在说明时未赋以初值,则系统自动赋予0值。而对自动变量没有赋初值,则其值是随机值。

 静态局部变量是一种生存期为整个源程序的量。虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用, 而且保存了上次被调用后留下的值。 因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成意外的副作用,因此仍以采用局部静态变量为宜。

<span style="font-size:14px;">//静态局部变量 int Fun(){ static int n = 0; ++n; return n; } for(int i = 0; i < 5; i++){ cout<<Fun()<<" "; } /*输出结果是1 2 3 4 5  //这里只初始化一次,每次调用都是依据上一次保存的值。</span>
 
<2>  静态全局变量

全局变量(外部变量)的说明之前再加static 就构成了静态全局变量。static全局变量只初使化一次,防止在其他文件单元中被引用。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。
 
综上:把局部变量改变为静态局部变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态全局变量是改变了它的作用域, 限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。
//测试静态全局变量

<span style="font-size:14px;">staticint value; voidfun(){ value= 0; ++value; } for(inti = 0; i < 5; i++){ fun(); cout<<value<<""; } /*输出1 1 1 1 1 。静态全局变量可以被多次赋值为0.</span>
 
<3>  static 函数
 
    Statci函数和普通函数作用域不同,它的作用只在定义它的本文件中。如果只在当前源文件中调用的函数,应说明为内部函数(static)。下面说下内部函数和外部函数:
    内部函数和外部函数:当一个源程序由多个源文件组成时,C语言根据函数能否被其它源文件中的函数调用,将函数分为内部函数和外部函数。
(1) 内部函数(又称静态函数)
如果在一个源文件中定义的函数,只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用,这种函数称为内部函数。定义一个内部函数,只需在函数类型前再加一个“static”关键字即可,如下所示:
static  函数类型  函数名(函数参数表) {}内部函数又称静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件。使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。
(2) 外部函数
外部函数的定义:在定义函数时,如果没有加关键字“static”,或冠以关键字“extern”,表示此函数是外部函数:
[extern]  函数类型  函数名(函数参数表) {}
调用外部函数时,需要对其进行说明:
              [extern]  函数类型  函数名(参数类型表)[,函数名2(参数类型表2)……];
 
static函数和普通函数的区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份复制品。在类中,静态成员函数不需要类的对象来调用,直接类调用:
classA
{
  static voidtest() {}
};
A::test();
静态成员函数只能访问静态成员变量,静态成员变量必须单独初始化。静态成员函数可定义为inline函数。
 
<4>  类的static 成员和函数

(1)当将类的某个数据成员声明为static时,该静态数据成员只能被定义一次,而且要被同类的所有对象共享。各个对象都拥有类中每一个普通数据成员的副本,但静态数据成员只有一个实例存在,与定义了多少类对象无关。静态方法就是与该类相关的,是类的一种行为,而不是与该类的实例对象相关。(2) 静态数据成员不能在类中初始化,也不能在类的构造函数中初始化该成员,因为静态数据成员为类的各个对象共享,否则每次创建一个类的对象则静态数据成员都要被重新初始化。
(3)  静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值。
(4) 静态成员的值对所有的对象是一样的。静态成员可以被初始化,但只能在类体外进行初始化。
(5) 它在对象中不占用存储空间,这个属性为整个类所共有,不属于任何一个具体对象
(6)静态函数成员必须通过对象名来访问非静态数据成员。另外,静态成员函数在类外实现时候无须加static关键字,否则是错误的。
(7)静态成员函数可以直接访问该类的静态数据和函数成员,而访问非静态数据成员必须通过参数传递的方式得到一个对象名,然后通过对象名来访问。


static关键字至少下列几个作用:   
(1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;   
(2)在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;   
(3)在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;   
(4)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;   
(5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。 
 
4.  外部变量
  在全局变量那已经提了external关键字,它就是告诉编译器存在一个变量或函数,即使在当前的编译的文件中没有看到它,这个变量或函数可能在另一个文件中或者在当前文件的后面定义。
  !!!如果一个变量i,它已经被定义为static,限定了作用域,这时你再用extern定义它是全局的,编译器就会报错。
 
5.  常量
在旧版C中,通常建立一个常量,必须使用预处理器:
#define  PI  3.14159
而C++用const来修饰常量,并加进了标准C中。一旦定义了常量,就不能再去修改它的值。如果初始化没给常量赋值,那它就是一个随机数,并且以后也不能给它赋值。C和C++中const的使用还是不同的,虽然const标记着“不会被改变”(见下题)。

<span style="font-size:14px;">在c++中, const int i=0 ; int* j = (int *)&i; *j =1; Printf(“ %d ,%d”,i,*j);      输出是多少? A 0,1 B 1,1 C 1,0 D 0,0</span>
答案是A,C++中i被定义为常量,在编译阶段就将常量i相当于宏定义,确定了它的初始值,C++在后面输出i时只是将i用初始值来做替换,和宏定义一样,而没有去读i的内存的值,所以输出i为0,而j是访问i的内存里的值,这里其实已经被改为1 了!而C中是运行时,const才确定值,将会输出它的值(被改变的),上题在C中运行结果就是1,1
大家可能对于const 修饰指针有点迷惑,下面几个:

<span style="font-size:14px;">int b=1; int c=2; const int* a1=&b; int const* a2=&b; int * const a3=&b; const int* const a4=&b; a1=&c; //这三个都正确 a2=&c; *a3=3;</span>
a1和a2一样,const都是修饰指针所指向的变量,即指针指向是常量,所以不能修改指针指向的内容,但可以修改指针的本身。
a3中const修饰的是指针本身,即指针本身是常量。所以不能修改指针本身,但可以修改指针指向的内容。
a4中const int* const类型。即不能修改指针本身也不能修改指向的内容。
 
   关于常量的补充:
  常量指针和指针常量的区别:

常量指针p1:指向的地址可以变,但内容不可以重新赋值,内容的改变只能通过修改地址指向后变换。   

    p1 = &a是正确的,但 *p1 = a是错误的。
指针常量p2:指向的地址不可以重新赋值,但内容可以改变,必须初始化,地址跟随一生。
    p2= &a是错误的,而*p2 = a 是正确的。

 

总结:对于const int * a , int * const a ,这样的,最简单的区分方式就是: const 如果在 *前面,那么这是常量指针,指针指向的地址的值不能改,但本身指针可以改,指向别的地址; 如果const 在*后面,那么这是指针常量,指针本身是常量,不可以改指向了,但可以修改它指向地址的值。


const关键字至少以下作用:   
(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;   
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;   
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;   
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的 成员变量;   
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。   
小结:从存储空间分配来说,全局变量、静态全局变量、静态局部变量都是在静态存储区(全局数据区)中分配空间的,而局部变量是在栈上分配空间的。常量存储在常量区。全局/静态存储区,是在程序编译时就已经分配好的,在整个运行期间都存在。而栈区系统自动释放(静态分配内存),预编译阶段就确定好了大小,栈区存放函数的参数值、局部变量的值。而new,malloc是在堆区申请内存,然后手动通过delete,free释放掉。不过要注意区分,new/delete是运算符,会调用构造和析构函数,而malloc/free是库函数,不会调用构造和析构。
 关于数据段:    
   BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。
数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。
栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。


原文:https://blog.csdn.net/zcyzsy/article/details/52207085 
 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

全局变量/局部变量/静态变量 的相关文章

  • sqli-labs (less-21)

    sqli labs less 21 进入21关 输入用户名与密码 发现跟20关基本一样 这里我们猜想也是在cookie的位置进入注入 利用Cookies Manager 抓取到cookie信息后 发现竟然是一串字母 这里就很懵了 但我们仔细
  • Flask简单Mock Server

    Mock Server充当的角色 Mock server在实际项目中的意义就相当于数据库 将我想要的数据返回给我就行 我并不关心你怎么逻辑处理的 一般的应用程序请求方式是GET和POST Flask自带的request使用 request
  • docker登陆mysql提示密码错误(转载)

    正文 进入mysql报错 1045 28000 原因 mysql5 7 首次安装后 需要修改root的默认密码才能使用 为了解决这个问题 来来回回试了很多遍 这里就不说过程了 下面记录下目前看正确的处理步骤 docker安装Mysql 1
  • python3.8安装dlib库(Windows下)

    自己在网上找了半天 各种让安装这个 让安装那个的 然后最后也没搞成 就试着这样搞 竟然 了 教程 电脑之前已经安装好Anaconda 打开里面的 cmd prompt 1 安装cmake pip install cmake 或者利用镜像下载
  • python之t分布

    import numpy as np from scipy stats import norm from scipy stats import t import matplotlib pyplot as plt print 比较t 分布与标
  • idea 使用lombok @Slf4j ,找不到log,解决方案

    1 安装lombok插件 2 如果安装了lombok插件 但是编译的时候还是找不到log 检查Annotation Processors gt Enable annotation processing 是否勾选
  • Vue+ant-design-vue 表格实现可拖动的伸缩列

    应客户要求 表格要实现列宽可以自主调节 用户可以根据自己的喜好 拖动列边框 左右拖动实现列宽的扩大和缩小 ant design vue官方文档中 table组件中提供了此功能的示例代码 于是我满怀开心的复制到了我的项目中 结果处处报错 红彤
  • MVVM简介

    目录 一 点睛 二 什么是MVVM 三 为什么要使用MVVM 四 MVVM的组成部分 五 MVVM实现者 一 点睛 View是视图层 也就是用户界面 前端主要由HTML和CSS来构成 为了更方便地展现ViewModel或者Model层的数据
  • Error occured while starting App. Original error: Permission to start activity denied.错误的解决方法

    错误异常 eclipse 1 LoginTest com csdn test LoginTest org openqa selenium WebDriverException An unknown server side error occ
  • ubuntu安装软件时出现,无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系

    转自https blog csdn net u012302488 article details 50013849 就是源的问题 并在 其他软件 选项卡中 去掉有问题等源 update下就可以了
  • 自动生成代码工具配置文件及技术点详解

    引言 之前发过一篇文章关于自动生成代码的项目 有小伙伴私信说要讲一下具体的思路与配置信息 现在满足一下大家的好奇 配置信息 generator properties配置文件中的具体内容可以看下方的配置信息说明 对应关系 key值 对应含义
  • 程序员一般可以从什么平台接私活?

    公众号 回复 代金券 免费获得腾讯云和阿里云代金券 分享该图文到朋友圈 截图发给本公众号后台 免费获得21个私活平台的快速入口 今天给大家介绍几个程序员可以接私活的平台 私活虽然也没有那么好做 一般私活的性价比远比上班拿工资的低 但是作为一
  • Shiro学习(5)-会话管理

    1 会话管理 相关的 API 会话监听器 配置案例 2 缓存 3 记住我
  • Spice-client源码简要分析

    下载地址 https www spice space org download gtk spice gtk 0 35 tar bz2 或者gitlab https gitlab com spice spice client官方的代码便是sp
  • tcount在哪个文件里_Stata统计分析命令

    Stata统计分析常用命令汇总 一 winsorize极端值处理 范围 一般在1 和99 分位做极端值处理 对于小于1 的数用1 的值赋值 对于大于99 的数用99 的值赋值 1 Stata中的单变量极端值处理 stata 11 0 在命令
  • service手动注入普通类

    package MonitorMessage util import org springframework beans BeansException import org springframework context Applicati
  • Intel TBB库+VS2019+WIN10配置

    目录 配置步骤 下载TBB库 配置环境变量 配置项目属性 在网上找TBB库的资料的时候发现相关资料太少 就想着开一个来记录自己的学习过程 不定期更新 配置步骤 如果能够接触到TBB库 相信你已经有了一定的编程基础 对VS等IDE也有了一定了
  • 【NLP】一文理解Seq2Seq

    seq2seq介绍 1 1 简单介绍 Seq2Seq技术 全称Sequence to Sequence 该技术突破了传统的固定大小输入问题框架 开通了将经典深度神经网络模型 DNNs 运用于在翻译 文本自动摘要和机器人自动问答以及一些回归预
  • 基于系统日志分析进行异常检测

    日志解析 https github com logpai logparser 异常检测 https github com logpai loglizer 预备知识 需要对逻辑回归 决策树 SVM PCA 聚类等有一些了解 论文原文 http
  • 让Redis突破内存大小的限制

    Redis虽然可以实现持久化存储 也是基于数据内存模型的基础之上 单机内存大小限制着Redis存储的数据量 有没有一种替代方案呢 本文介绍一款笔者使用的采用New BSD License 许可协议的软件 SSDB 官网地址 http ssd

随机推荐

  • EF常见数据库连接字符串示例

    数据库类型 字符串 Sqlite Data Source Furion db MySql Data Source localhost Database Furion User ID root Password 000000 pooling
  • 第四周课程总结&试验报告(二)

    实验二 Java简单类与对象 实验目的 掌握类的定义 熟悉属性 构造函数 方法的作用 掌握用类作为类型声明变量和方法返回值 理解类和对象的区别 掌握构造函数的使用 熟悉通过对象名引用实例的方法和属性 理解static修饰付对类 类成员变量及
  • python-gif图生成

    python 用几行代码便生成gif图 代码如下 import imageio filenames 1 jpg 2 jpg 3 jpg images for filename in filenames images append image
  • C语言

    1024G 嵌入式资源大放送 包括但不限于C C 单片机 Linux等 关注微信公众号 嵌入式大杂烩 回复1024 即可免费获取 C语言类型 C的类型分为 对象类型 char int 数组 指针 结构体等 函数类型 不完全类型 什么是不完全
  • 机器学习——支持向量机

    机器学习 支持向量机 一 定义 二 基本概念 1 线性可分 2 分割超平面 3 超平面 4 点相对于分割面的间隔 5 间隔 6 支持向量 三 寻找最大间隔 1 分隔超平面 2 如何决定最好的参数 3 凸优化 4 拉格朗日对偶 拉格朗日乘子法
  • Java的String类

    Java中String是常量 其对象一旦创建完毕就无法 改变 当使用 拼接字符串时 会生成新的String对象 而不是向原有的String对象追加内容 对于Java 维护着一个字符串池的概念 String pool String s aaa
  • 服务环境搭建-Traefik网关服务

    服务环境搭建 Traefik网关服务 1 说明 Traefik网关服务用于提供一个实现反向代理 中间件鉴权 服务负载均衡 与服务发现的环境 2 反向代理 2 1 基本概念 EntryPoints 入口点是进入Traefik的网络入口点 它们
  • Python3-面向对象

    Python之面向对象 面向对象 走进对象的世界 类的基本操作 定义 属性 方法 三性 封装 继承 多态 封装 继承 多态 单例 练习 面向对象 对象创建 属性 定义 封装 继承 多态 单例 走进对象的世界 类的基本操作 定义 面向对象 程
  • 未来的工作都被计算机代替,未来10年,50%的工作将被机器取代?而这些职业却无法被取代...

    声明 原创不易 禁止搬运 违者必究 50 的工作将被机器人取代 时代的车轮在前进 更新换代也越来越快了 总是有新兴行业的诞生 也总是有传统的行业退出 变化成为了时代发展的一个重要标志 创建阿里巴巴的马云 之所以能够如此的成功 不得不说他的前
  • Vue中BootStrap和分页组件 实现分页功能(页码过多时带省略号)

    更新 其实vue中的分页插件结合上 spring data jpa 使用的效果非常好 使用更加方便 vue组件中 div class box footer no border div
  • 微信小程序:云开发·初探四(数据库操作)

    The course of true love never did run smooth 真爱无坦途 新建集合 1 打开云开发控制台 数据库 2 添加集合users 添加代码 onAdd function const db wx cloud
  • DX90SDK SDK源码分析(二) 推模式的例子

    转载请标明是引用于 http blog csdn net chenyujing1234 例子代码 编译工具 VS2005 http www rayfile com zh cn files 46611607 78a2 11e1 ac18 00
  • Makefile的函数调用详解

    1 Makefile的函数调用语法 Makefile的函数调用格式
  • 高德地图的简单使用:点击标记获取经纬度和详细地址

    准备工作 1 先进入高德开发平台注册登录 2 进入地图 js Api 按照步骤申请key 3 使用npm安装依赖包 npm i amap amap jsapi loader save 4 高德api 都有说明 下面看下我实现的功能和代码 弹
  • hutool的HttpRequest.post的使用-包括上传文档等多个传参【总结版本】

    首先hutool已经为我们封装好了远程调用的接口 我们只要将对应的传参和方式对应填写即可 hutool官方文档 1实际应用 post 常见的使用json传参 contend type为application json RequestMapp
  • 计算机键盘运算符号输入,电脑上感叹号怎么打出来(电脑键盘符号大全)

    01 在使用键盘输入标点符号时 大部分都可以直接通过键盘按键或者按Ctrl 键盘按键直接输入 比如按下shift 1 就可以输入感叹号 中文状态下按反斜杠键就可以输入顿号 其实在键盘上的很多同一个按键 中文状态下和英文状态下是不一样的 如下
  • King 【POJ - 1364】【差分约束+SPFA+卡了判负环的进队次数】

    题目链接 题目大意 n个数的一个序列 m个条件 si ni oi ki 代表了序列中第si个数到第si ni个数的和大于或小于ki gt 大于 lt 小于 问是否存在相悖的约束 op为gt时 sum si ni sum si 1 gt ki
  • 宽带远端服务器无响应什么意思,宽带远程服务器无响应

    宽带远程服务器无响应 内容精选 换一换 已成功添加目标环境 如果目标环境处于在线状态 只允许修改目标环境名称 如果目标环境处于离线状态 可以输入登录远程服务器的用户名和密码重新部署分析辅助软件 如果目标环境处于在线状态 只允许修改目标环境名
  • 10.12技术沉淀

    cpp 编写程序实现ADT表的定义 及常用操作 1 判断表是否为空 2 获取第i个节点的内容 3 删除 4 插入 include
  • 全局变量/局部变量/静态变量

    1 全局变量 全局变量是在所有函数体的外部定义的 程序的所有部分 其他文件的代码 都可以使用 全局变量不受作用域的影响 全局变量的生命周期一直到程序的结束 全局变量是静态存储方式 如果在一个文件中使用extern关键字来声明另一个文件中存在