链接和作用域

  前边我们已经开始创建由多个文件构成的项目,是时候再来讨论下更复杂的变量作用域了。
  简单的理解,变量的作用域就是你可以在什么范围内访问这个变量。
  地球人都知道,一个在任何函数之前定义的变量可以在任何一个函数里使用(这是一个全局变量),而在某个函教里定义的变量只能在那一个函数里使用(这是一个局部变量)。
  那么,当一个项目由多个文件构成时,变量的作用域也会受到一定的影响!

  与作用城有关的另一个概念是链接,当你同时编译多个文件时:

-g++ -o test main.cpp rational.cpp

  每个源文件都被称为一个翻译单元(translation unit),在某一个翻译单元里定义的东西在另一个翻译单元里使用正是链接发挥作用的地方。作用域、链接和存储类是相互关联的概念,它们有许多共同的术语,只是观察和描述问题的备度不同罢了。

存储类(storage class)
  每个变量都有一个存储类,它决定着程序将把变量的值存储在计算机上的神马地方、如何存储,以及变量应该有着怎样的作用域。
  默认的存储类是auto(自动),但你不会经常看到这个关键字,因为它是默认的,阴魂不散的!
  自动变量存储在称为栈(stack)的临时内存里并有着最小的作用域,当程序执行到语句块或函数末尾的右花括号时,它们将被系统回收(栈回收),不复存在。

  与auto不同的是static,static变量在程序的生命期内将一直保有它的值而不会消亡,因为它们是存储在静态存储区,生命周期为从申请到程序退出(和全局变量一样)。
  另外我们稍后就会提到的,一个static变量可以有external或internal链接。
  第三种存储类是extern,它在有多个翻译单元时非常重要。这个关键字用来把另一个翻译单元里的某个变量声明为本翻译单元里的一个同名全局变量。

  注意,编译器不会为extern变量分配内存,因为在其他地方已经为它分配过内存。
  用extern关键字相当于告诉编译器:“请相信我,我发誓我知道这个变量在其他翻译单元里肯定存在,它只是没在这个文件里声明而已!”
   还有一个存储类是reqister,它要求编译器把一个变量存储在CPU的寄存器里。但有着与自动变量相同的register变量存储速度最快,但有些编译器可能不会许使用这类变量。

变量的链接和作用域
  链接是一个比较梁奥的概念,所以我们尽可能以浅显的文字来解释它。
  在使用编译器建议程序时,它实际上是由3个步骤构成:
    1.执行预处理器指令;
    2.把.cpp文件编译成.0文件(二进制代码,即翻译单元);
    3.把.0文件链接成一个可执行文件。
  如今的编译器都是一次完成所有的处理,所以你看不到各个步骤(学习Win32汇编的童鞋懂~)。

  步骤一前边我们已经讨论过:执行预处理指令,例如把#include指令替换为相应的头文件里的代码,总的效果是头文件里的代码就像从一开始就在.cpp文件里似的。
  步骤二是我们司空见惯的事倩:把C++代码转换为一个编译目标文件,在这一步骤里,编译器将为文件里的变量分配必要的内存并进行各种错误检查。

  如果只有一个C++源文件,步骤三通常只是增加一些标准库代码和生成一个可执行文件。
  但当你同时编译多个源文件来生成一个可执行文件的时候,在编译好每一个组件之后,编译器还需要把它们链接在一起才能生成最终的可执行文件。
  当一个编译好的对象(即翻译单元)引用一个可能不存在于另一个翻译单元里的东西时,潜在的混乱就开始出现了。

  链接分为三种情况,凡是有名字的东西(函数、类、常量、变量、模板、命名空间,等等)必然属于其中之一:外连接(external),内链接(internal)和无链接(none)。
  外链接的意思是每个翻译单元都可以访问这个东西(前提是只要它知道有这么个东西存在)。
  普通的函数、变量、模板和命名空间都有外链接。
  就像main.cpp可以使用rational.cpp文件里定义的类和函数一样,其实我们一直在使用,只是今天我们来次总结。

  说到变量,你可以这样试一试:

//this.cpp
int i1 =1;

//that.cpp
int i2 = i1;

  不用试了,一看就有问题,对不对?!在编译that.cpp文件时,编译器并不知道i1变量的存在。为了解决这个问题,我们可以在that.cpp里使围县extern关键字去访问第一个翻译单元的变量。 

//this.cpp
int i1 =1;

//that.cpp
extern int i1;
int i2 = i1;

  内链接的含义是:在某个翻译单元里定义的东西只能在翻译单元里使用,在任何函数以外定义的静态变量都有内链接。

//this.cpp
static int d = 8;

//that.cpp
static int d = 9;

  这两个文件各有一个同名的变量,但它们是毫不相干的两样东西,并不会引起冲突。

  最后,在函数里定义的变量只存在于该函数的内部,根本没有任何链接(none)。

Example工程:

header.h

#ifndef HEADER_H
#define HEADER_H

unsigned long returnFactorial(unsigned short num);

//不要在头文件内定义变量,不然可能出现多次定义错误,只需声明便可!
//如果必须定义,则定义为static类型 
static const unsigned short headerNum = 5;

#endif

 

that.cpp 

#include "header.h"

unsigned short thatNum = 8;
bool printMe = true;

unsigned long returnFactorial(unsigned short num)
{
	unsigned long sum = 1;
	
	for(int i = 1; i <= num; i++)
	{
		sum *= i;
	}
	
	if(printMe)
	{
		return sum;
	}
	else
	{
		return 0;
	}
}

main_this.cpp

#include "header.h" 
#include <iostream>

extern unsigned short thatNum;
static bool printMe = false;//static限定printMe只有内链接 

int main()
{
	unsigned short thisNum = 10;
	
	std::cout<<thisNum<<"! is equal to "<<returnFactorial(thisNum)<<std::endl;
	std::cout<<thatNum<<"! is equal to "<<returnFactorial(thatNum)<<std::endl;
	std::cout<<headerNum<<"! is equal to "<<returnFactorial(headerNum)<<std::endl;
	
	if(printMe)
	{
		std::cout<<"帅帅的";
	}
	
	return 0;
}

  结果:

10! is equal to 3628800
8! is equal to 40320
5! is equal to 120
请按任意键继续. . .

  

原文地址:https://www.cnblogs.com/tianqizhi/p/10497602.html