1. 声明和定义

​  当定义一个变量的时候,就包含了对该变量声明的过程,同时在内存张申请了一块内存空间。如果在多个文件中使用相同的变量,为了避免重复定义,就必须将声明和定义分离开来。定义是创建与名字关联的实体。声明是让名字为程序所知,当一个文件想要使用其他文件定义的某个变量,则必须包含对那个文件的声明:

  1. 函数和变量的声明不会分配内存, 但是定义会分配相应的内存空间

  2. 函数和变量的声明可以有很多次, 但是定义最多只能有一次

  3. 函数的声明和定义方式默认都是 extern 的, 即函数默认是全局的

  4. 变量的声明和定义方式默认都是局部的, 在当前编译单元或者文件内可用

  了解声明和定义对static和extern的理解有辅助作用。比如extern就是在一处定义,其他文件都只需要声明即可,不可重复定义。

2. static& extern

2.1 static

  一般局部变量是存储在栈区的,局部变量的生命周期在其所在的语句块执行结束时便结束了。但如果用static修饰局部变量,那么这个变量就不会存储在栈区而是放在静态数据区,其生命周期会一直持续到整个程序结束,该变量只在初次运行时进行初始化,且只进行一次,但是它的作用域只能是在函数里面如下:

void print(){
static int z = 100;
z++;
cout << z <<endl;
}

int main(){
print();
print();
print();
return 0;
}

局部静态变量z只能在本文件的print函数里面访问,一旦超出作用域范围,就无法访问。

  如果是static修饰的全局变量,且实现的函数写在头文件(h)中,在其他文件也可以访问,如下:

//  a.h
#ifndef A_H
#define A_H
#include<iostream>
using namespace std;
static char str[] = "hello";
namespace sextern {
void print1();
void Fun1();
void Fun1(){
str[0] = 'l';
}
void print1(){
cout << "value " << str <<endl;
cout << "address " << &str <<endl;
}
}
#endif // A_H


//b.h
#ifndef B_H
#define B_H
namespace sextern{
void Fun2(){
str[0] = 'o';
}
void print2(){
cout << "value " << str <<endl;
cout << "address " << &str <<endl;
}
#endif // B_H

//main.cpp
#include "a.h"
#include "b.h"
using namespace sextern;
int main(int argc, char *argv[])
{
sextern::Fun1();
print1();
sextern::Fun2();
print2();
print1();
return 0;
}

//结果如下
/*
* value lello
* address 0x601058
* value oello
* address 0x601058
* value oello
* address 0x601058
* 按 <RETURN> 来关闭窗口...
*/

发现将static全局变量写在头文件中,所有文件的头文件的操作都会共享这个变量。

​   但如果是在源文件(cpp)中去操作这个静态全局变量,则这个静态全局变量只能在当前文件有效,但是在另外一个文件访问此静态变量,会是该变量初始的默认值,不会是其他文件中修改的值,虽然它们有相同的初始内容,但是存储的物理地址并不一样。如下:

//a.h
#ifndef A_H
#define A_H
#include<iostream>
using namespace std;
static char str[] = "hello";
namespace sextern {
void Fun1();
void print1();
}
#endif // A_H

//a.cpp
#include "a.h"
namespace sextern {
void Fun1(){
str[0] = 'l';
}
void print1(){
cout << "value " << str << endl;
cout << "address " << &str <<endl;
}
}

//c.h
#ifndef C_H
#define C_H
#include<iostream>
using namespace std;
namespace sextern {
void Fun3();
void print3();
}
#endif // C_H

//c.cpp
#include "c.h"
#include "a.h"
namespace sextern {
void Fun3(){
str[0] = 'o';
}
void print3(){
cout << "value " << str <<endl;
cout << "address " << &str <<endl;
}
}


#include "a.h"
#include "c.h"
using namespace sextern;
int main(int argc, char *argv[])
{
sextern::Fun1();
print1();
sextern::Fun3();
print3();
print1();
return 0;
}

//结果如下
/*
* value lello
* address 0x602064
* value oello
* address 0x60205e
* value lello
* address 0x602064
* 按 <RETURN> 来关闭窗口...
*/

在a.h的头文件中定义了一个静态的全局变量x,不同文件的函数fun1和fun3会为每个包含该头文件的cpp都创建一个全局变量,但他们都是独立的,只在该cpp文件共享该变量。所以一般定义static全局变量时,都把它放在原文件中而不是头文件,从而避免多个源文件共享,就不会给其他模块造成不必要的信息污染。如果想要在不同文件共享同一个全局变量,这个时候就要用到extern。

2.2 extern

  当在某个文件定义了一个全局变量,如果要在另一个文件去使用该变量,如果再次去定义,则会出现重复定义的问题,这个时候就需要使用到声明,对该变量的声明告诉编译器该变量在其他文件中已经定义,在此处要去引用它。上述的代码改成如下形式:

//b.h
#ifndef B_H
#define B_H
char str[] = "hello"; //定义一个全局变量
#endif // B_H

//a.h
#ifndef A_H
#define A_H
#include<iostream>
using namespace std;
extern char str[];
namespace sextern {
void Fun1();
void print1();
}
#endif // A_H

//a.cpp
#include "a.h"
namespace sextern {
void Fun1(){
str[0] = 'l';
}
void print1(){
cout << "value " << str << endl;
cout << "address " << &str <<endl;
}
}

//c.h
#ifndef C_H
#define C_H
#include<iostream>
using namespace std;
extern char str[];
namespace sextern {
void Fun3();
void print3();
}
#endif // C_H

//c.cpp
#include "c.h"
namespace sextern {
void Fun3(){
str[0] = 'o';
}
void print3(){
cout << "value " << str <<endl;
cout << "address " << &str <<endl;
}
}

//结果如下
/*
* value lello
* address 0x602058
* value oello
* address 0x602058
* value oello
* address 0x602058
* 按 <RETURN> 来关闭窗口...
*/

参考资料

详解C/C++中的static和extern

C/C++中extern关键字详解

最新文章

  1. VM12.1.1 下载 序列号
  2. Oracle Dataguard的原理与基本配置
  3. 如何预览github中的html页面
  4. DEDECMS如何修改数据库密码以及忘记了后台密码怎么办
  5. CF 136B Ternary Logic
  6. mybatis update语句参数正常, 数据没有更新
  7. 3. QT窗体间值的传递(续)
  8. 【Cocos得知】技术要点通常的积累
  9. ionic结合HTML5实现打电话功能
  10. BotVS开发基础—2.5 绘制图表
  11. Easyui 实现点击不同树节点打开不同tab页展示不同datagrid表数据设计
  12. Redis配置主从复制
  13. 2019.1.17 homework
  14. JAVA自学笔记22
  15. HBase的写事务,MVCC及新的写线程模型
  16. Java 利用 poi 生成 Excel文件的通用例子
  17. Java编程的逻辑 (63) - 实用序列化: JSON/XML/MessagePack
  18. 10.Set 和 Map 数据结构
  19. etcd 分布式数据库概念初探
  20. VBA 语言基础

热门文章

  1. Delphi多线程下的ADO编程
  2. 数据绑定(七)使用ObjectDataProvider对象作为Binding的Source
  3. 关于阿里云centos7安装svn,客服端无法链接的问题
  4. 【Linux】scp“免密” 远程copy较多文件
  5. centos 6.5 搭建ftp 服务器(vsftpd的配置文件说明)
  6. QuickReport根据每行的内容长度动态调整DetailBand1的行高
  7. Android零基础入门第85节:Fragment使用起来非常简单
  8. MongoDB.Driver 管道 Aggregate
  9. Spring MVC的工作原理,我们来看看其源码实现
  10. Hadoop集群(第5期)SecureCRT使用