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关键字详解

extern和static区别的更多相关文章

  1. 全局变量,extern和static以及命名空间的区别

    全局变量,extern和static以及命名空间的区别        全局变量只是在声明它的文件中有效,假如在另一个文件中声明定义了一个相同名称的全局变量,则在后续使用这两个变量的时候会产生名字上的冲 ...

  2. 话说extern和static

    以前对extern.static的一些东西一直模棱两可.今天好好来梳理了一番.. static关键字 被static修饰的变量或函数称之为静态成员.函数. 存储位置:static修饰的变量存放在静态区 ...

  3. 链接(extern、static关键词\头文件\静态库\共享库)

    原文链接:http://www.orlion.ga/781/ 一. 多目标文件的链接 假设有两个文件:stack.c: /* stack.c */ char stack[512]; int top = ...

  4. IOS的变量前加extern和static字段

    IOS的变量前加extern和static字段 前一阵子,做项目的时候到网上找Demo,打开运行的时候发现其中变量前有关键字extern和static,所以我研究了一下子 对于extern来说可以理解 ...

  5. [C]控制外部变量访问权限的extern和static关键字

    一.extern 概述 编译器是由上至下编译源文件的,当遇到一些函数引用外部全局变量,而这个变量被定义在该函数声明主体的下方,又或者引用自其它的编译单元,这个情况就需要extern来向编译器表明此变量 ...

  6. C++中extern “C”含义及extern、static关键字浅析

    https://blog.csdn.net/bzhxuexi/article/details/31782445 1.引言 C++语言的创建初衷是“a better C”,但是这并不意味着C++中类似C ...

  7. 存储类型auto,static,extern,register的区别 <转>

    变量和函数的属性包括数据类型和数据的存储类别,存储类别指数据在内存中存储方式(静态和动态),包含auto,static,register,extern四种. 内存中.具体点来说内存分为三块:静态区,堆 ...

  8. iOS中的extern与static

    1.extern #import <Foundation/Foundation.h> extern NSString *DBDefaultName; @interface DataBase ...

  9. C语言关键字register、extern、static

    C语言中: 一.register变量 关键字regiter请求编译器尽可能的将变量存在CPU的寄存器中.有以下几点注意的地方. register变量必须是能被CPU寄存器所接受的类型,这通常意味着re ...

随机推荐

  1. WPF中任意Object的XAML代码格式化输出

    原文:WPF中任意Object的XAML代码格式化输出 有时候,我们需要将WPF中的控件自身的XAML代码输出成文本,那么,我们可以使用System.Windows.Markup.XamlWriter ...

  2. SQLServer 以备份初始化订阅

    原文:SQLServer 以备份初始化订阅 在创建事务复制时,如果发布数据库很大,使用快照初始化时,将等待很久,如果出现问题可能又得重新初始化.使用备份初始化会省很多时间,但是数据库在创建发布订阅期间 ...

  3. UWP项目生成安装包远程安装在树莓派上

    原文: UWP项目生成安装包远程安装在树莓派上 哎,好纠结啊!如果这个名字写的太长,会显得太繁琐,如果写的短又好像说不清楚,我这语言表达水平实在是令人担忧啊!不过应该能够明白啥意思吧!因为对这个感兴趣 ...

  4. 毕设(五)ListView

    ListView 控件可使用四种不同视图显示项目.通过此控件,可将项目组成带有或不带有列标头的列,并显示伴随的图标和文本. 可使用 ListView 控件将称作 ListItem 对象的列表条目组织成 ...

  5. 给变量赋值,程序会跳到 HardFault_Handler的问题

    原因:变量属于指针,该指针没有初始化

  6. qt5.6 webengine兼容xp的编译方法

    http://www.qtcn.org/bbs/read-htm-tid-62470.html http://stackoverflow.com/questions/31678657/qtwebeng ...

  7. 使用MSYS2环境中编译Qt5.5.0的补丁

    Qt的configure脚本对MinGW静态编译支持不太完善,总有这样那样的问题.如果你不嫌麻烦,而且可以接受高版本的Qt的话,可以考虑使用我做的补丁在MSYS2环境中编译.Qt5.4.2的补丁 Qt ...

  8. 【Eclipse常见错误】-Cannot return from outside a function or method

    最近发现myeclipse10中有几处bug 比如: Cannot return from outside a function or method onClick="return chec ...

  9. KM算法 详解+模板

    先说KM算法求二分图的最佳匹配思想,再详讲KM的实现.[KM算法求二分图的最佳匹配思想] 对于具有二部划分( V1, V2 )的加权完全二分图,其中 V1= { x1, x2, x3, ... , x ...

  10. .NET错误:未找到类型或命名空间名称

    现象:编译项目时提示未找到类型或命名空间名称"... " 解决方法:如果是未找到类型,检查是否引用了类型所在的命名空间,使用using指令:如果是未找到命名空间,那么检查是否引用了 ...