C++中的全局变量,静态变量与局部变量

在我的上一篇博客中,我讲了如何编译多个C++源代码并且把它们链接成一个整体的可执行文件。当我们试图编写稍微大一点的程序的时候,这种分离编译的好处就会变得很明显。那么另一个很重要的问题就是:在我们分离编译的时候,这些不同的源代码之间应该怎么配合起来工作呢?比如说我需要这几份源代码共享一些数据,我应该怎样安全并且高效的实现这种共享呢?这就要涉及到C++的一个很底层的机制,那就是变量类型的分级。也就是我们的全局变量,静态变量以及局部变量的区别和联系。 

  这些东西可能听起来很简单,但是如果你不谨慎地处理它们的话,它们可能导致很多的麻烦。C++为了调和这些关系甚至提出了名字空间这一新的抽象机制。

  我们先从一般人最熟悉的局部变量开始。局部变量在C++里面又叫自动存储变量(automatic varible),这是因为,在一个函数栈中,当你声明这个变量的时候,编译器总是会在函数栈里自动的为这个变量分配一块存储空间。需要注意的是这个局部变量的作用域总是从它被声明的那一行开始,然后到下一个右大括号 } 结束。并且在它的作用域内它可以覆盖所有与它同名的变量。比如考虑下面的代码:

复制代码

 1 #include<iosteam>
 2 int main(){
 3     int a=5;
 4     {
 5         std::cout<<a<<std::endl;
 6         int a=4;
 7         std::cout<<a<<std::endl;
 8     }
 9    std::cout<<a<<std::endl;
10     return 0;
11 }

复制代码

  这段代码会先输出5然后输出4,最后又会输出5。这个时候如果我们在程序块里面想要使用外面的声明的同名变量怎么办呢?比如在我们上面的例子中,如果我们想要在最里面的那个大括号中使用第3行定义的变量a,在这种情况下我们做不到这件事情,但是如果想要使用一个全局变量是可行的。

  当我们只有一个源代码文件的时候我们只需要把这个变量的声明放在所有的函数(包括 main 函数)之外,这个变量就会自动的成为一个全局变量。但是在分离编译的时候这样会产生一个问题,那就是我们知道在C++程序设计语言中,一条很基本的法则就是一个变量只能被声明一次,但是如果你假如要在n个源代码文件中使用同一个变量,那么最后链接起来的程序代码里面这个变量就会被声明n次。解决的这个问题的一个办法是我们可以把这个变量的定义和声明放到头文件里面去,然后利用头文件的 #ifndef 机制来规避掉这个问题。但是显而易见这样做是一件很麻烦的事情。

  另一种解决这个问题的办法就是使用 extern 关键字,你只需要在你习惯的全局变量前面加上 extern 关键字,这个关键字相当于告诉编译器这里不是这个变量的声明,让编译器到其他的文件里面去找这个变量的声明。需要注意的是,不管有几个文件 extern 了这个变量,只有一个文件能够包含这个变量的声明(即不带 extern 的)。

  为了清楚地说明这一点,我们给出下面这个例子:

1

2

3

4

5

6

//cprogram2.cpp

#include<iostream>

extern int a;

void print(){

        std::cout<<a;

}

  

1

2

3

4

5

//cprogram.cpp

int a =6;

void print();<br>int main(){

    print();

}

  然后在终端上编译,链接,执行:

1

2

3

g++ cprogram.cpp cprogram2.cpp

g++ cprogram.o cprogram2.o -o cprogram

./cprogram

  最后我们可以看到程序输出了我们期望的6,这说明我们实现了变量 a 跨文件的传递。

 

   我们刚刚提到了在一个代码块内部存在一个同名的局部变量的时候仍然可以使用全局变量,只需要在变量名前面加上两个冒号 :: 就行了,双冒号这个记号在编写类以及使用名字空间的时候大有作为。

  C++中另一种变量叫做静态变量,一般在声明的前面加上static 关键字来标记它。它的一种主要用法是像全局变量一样声明在所有的函数之外,这样便可以在声明它的那个文件内部覆盖掉全局变量,比如我们把上面的 cprogram2.cpp 修改成下面这个样子:

1

2

3

4

5

6

//cprogram2.cpp

#include<iostream>

static int a=5;

void print(){

        std::cout<<a;

}

  同样的编译链接之后最后执行出来的输出会变成5。尽管我们声明了全局变量 a=6 ,但是我们这里成功的用静态变量覆盖了它。当然这里的另一个原因也是因为由于我们在全局作用域声明了一个静态变量了那么我们便不能再使用 extern 关键字提示编译器有一个同名的全局变量了。同样,在这个文件内部的所有程序块都可以通过在变量名前面加 :: 来访问这个静态变量。关于静态变量比较有意思的是它的另一种用法。

  如果我们在一个函数内部声明然后初始化一个静态变量的话,这个静态变量不会因为函数的再次调用而被重新初始化,甚至当这个函数一次调用结束之后,这个静态变量仍然存在于我们的程序当中,举个例子,比如说我们有一段 test.cpp 的源代码:

1

2

3

4

5

6

7

<em id="__mceDel">// test.cpp<br>#include<iostream>

void print(){

   static int a=1;<br>  int b=1;     

   std::cout<<"a is"<<a<<std::endl;<br>  std::cout<<"b is"<<b<<std::endl;

   a++;

}<br>int main(){<br>  print();<br>  print();<br>  print();<br>  return0;<br>}<br>

</em>

  执行之后我们会发现变量 a 随着 print() 的每一次执行都会增加1,但是变量 b 一直是1没有发生过变化。这是因为当我们在函数里面声明一个静态变量的时候,编译器并不是在函数栈里面为存储这个变量,因此这个变量也不会随着函数栈的弹出而被弹出,而是一直被保存在一块静态变量区里面,当这个函数下次调用的时候仍然可以去修改它。但是注意到当这个函数退出以后。除非我们再次调用这个函数,否则我们将没有办法去访问这个静态变量。实现这一点的关键是当这个函数退出之后,这个静态变量的作用域也就结束了,为了重新回到这个静态变量的作用域,我们需要重新调用这个函数,从某种意义上来说,这相当于是这个函数认领了这个静态变量的作用域。事实上我们可以看到通过这个技术我们变相的实现了对这个静态变量的封装,在面向对象程序设计里面我们会广泛地运用到这种技术。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值