变量的作用域和连接性

|   版权声明:本文为博主原创文章,未经博主允许不得转载。

  在C++中有三种不同方案处理存储数据,这三种方案的区别主要是数据在内存中存储的时间。

  >>.自动存储的持续性:在函数中声明的变量(局部变量)的存储性全部都是自动的。在程序执行的它的所属函数或者代码块的时候被创建,在函数或者代码块执行完成之后,保存它们数据的内存将自动的释放。

  >>.静态存储的持续性:使用static声明或者全局变量则为静态存储性的静态,在程序的整个运行的过程中存储数据的内存都不会释放,只有在程序运行结束的时候它们的内存才会释放。

  >>.动态存储的持续性:用new运算符分配的内存将一直存在,除非是程序结束自动的释放或者使用delete运算符将其释放。这种内存的存储持续性为动态。

  动态变量:

  在函数体中声明的变量和函数的参数都是动态变量,它的存储持续性是动态的,作用域是局部的,没有链接性。这是因为在一个函数(或者形式参数)中声明变量a,同时在另一个函数(或者形式参数)中声明变量a,我们知道这两个变量是相互独立会不影响的。只有在定义的函数内和相对应的作用域中才能使用它们。

  动态变量的作用域,如下将通过变量的地址来进行说明:

#include <iostream>
using namespace std;

void test1(int a)
{
	cout << "  test1: &a = " << &a << endl;
}

int main(void)
{
	int a = 5;

	cout << " main_1: &a = " << &a << endl;

	test1(a);

	{
		int a = 10;
		cout << " main_2: &a = " << &a << endl;
	}

	cout << " main_1: &a = " << &a << endl;
	
	return 0;
}

地址信息如图:

如图可以看出动态变量的作用域有如下的特点:

(1)、在一个函数的内部的变量(形参)它的作用域为函数的中该变量定义处开始到函数体结束(形参的话就是整个函数的代码块的作用域)

(2)、在一个大的代码块中,有两个花括号嵌套在一起,如果有两个同名的变量(一个相对于另一个处于外部代码块,另一个处于最里层的花括号中),那么此时最里层的花括号变量的作用域范围就是最里层的花括号区域,而外部的变量的作用域为最外层花括号区域,只是在有内部同名变量的区域,此时外部变量的会被覆盖而已。看main_1和main_2的地址可知。

(3)、在函数中的变量和mian函数中的变量同名是不影响的,因为他们两个是独立的。看main_1和test1的地址可知。

看下图:

 

  自动变量的持续性和自动变量的保存结果是有关的,自动变量用栈进行存储。因为对于一个函数内部代码块的变量来说,首先遇到的变量,它的作用域绝对是比后面定义的变量的作用域范围要大,因此这类似栈的结构先进先出。同样对于函数内部调用函数的变量也是一样。通过入栈来控制作用域的范围。就上图的一个变量入栈退栈图:

  在自动变量中还有一个特殊的变量,叫做寄存器变量,这个主要是为了将某些常访问的变量设置为register,告诉编译器要用cpu的寄存器来存放该数据,以此缩短访问该变量的时间。

   静态变量:

   静态变量主要是全局变量,通过static声明的变量。静态变量有三种类型:外部链接性型静态变量(其他文件可访问),外部链接性型静态变量(只能在本文件中使用)和无链接性型静态变量(只能在当前的函数或者代码块中使用)。这三种的持续性是整个程序运行的期间(也就是三种类型的静态变量的执行期间是整个程序运行的期间),相对于动态变量来说,这三种静态变量的生命周期比动态变量要长。

三种链接性静态变量的创建方式如下:

////////////////////////      1-外部链接性静态变量

#include <iostream>
using namespace std;


//外部链接性的静态变量
int out_link_static_value = 10;

int main(void)
{
      ......
      return 0;
}


////////////////////////      2-内部链接性静态变量

#include <iostream>
using namespace std;


//内部链接性的静态变量(就是在全局变量的前面添加static)
static int inner_link_static_value = 10;

int main(void)
{
      ......
      return 0;
}


////////////////////////      3-无链接性静态变量

#include <iostream>
using namespace std;

//定义无链接性的静态变量,是在函数体或者代码块在定义的变量,且必须有static修饰符。
void noLinkFunc()
{
       static non_link_static_value = 10;
}

int main(void)
{
      ......
      return 0;
}

  静态变量的初始化也分为三种方式:默认初始化,常量表达式初始化,动态初始化。默认初始化表示的是编译器自动将未初始化的静态变量符初始值0;常量初始化直接赋数值,动态初始化是指在编译阶段不能得到足够的信息初始化的静态变量,要需要等待在链接程序的时候才知道。

//////////////////////       默认初始化
static int int_value;


//////////////////////       常量初始化
static int int_value = 10;


//////////////////////        动态初始化
static int int_value = 5*sqrt(16);

>>.外部链接性:

代码示例:

////////////////////                       A.cpp

//定义外部链接性变量
int a = 10;
int b = 20;


////////////////////                       B.cpp

//定义外部链接性变量
int c = 30;
int d = 40;


////////////////////                       C.cpp
#include <iostream>
using namespace std;


int main(void)
{
	//使用外部链接性的变量前必须先声明:在前面添加修饰符extern
	extern int a;
	extern int b;
	extern int c;
	extern int d;

	cout << "A.h: a = " << a << endl;
	cout << "A.h: b = " << b << endl;
	cout << "B.h: c = " << c << endl;
	cout << "B.h: d = " << d << endl;
	
	return 0;
}

  如上图如果要在多个文件中使用外部变量,只需要在其中的一个文件中定义该变量(如:A.cpp B.cpp,可以省略extern,但是常量静态变量定义外部链接性时必须要有extern),但在使用的该变量的其他所有文件中,都必须使用关键字extern声明它(如:C.cpp)。

  但是如果我们在使用全局变量的地方没有声明extern,那么将会出现错误,如下图:

 1 int main(void)
 2 {
 3     //使用外部链接性的变量前必须先声明:在前面添加修饰符extern
 4     int a;
 5     int b;
 6     int c;
 7     int d;
 8 
 9     cout << "A.h: a = " << a << endl;
10     cout << "A.h: b = " << b << endl;
11     cout << "B.h: c = " << c << endl;
12     cout << "B.h: d = " << d << endl;
13     
14     return 0;
15 }
Erroe Code

  如果我们在两个不同的文件中,定义两个同名的变量,也将出现错误(重定义),如下图:

 1 ///////////////////////          A.cpp
 2 //定义外部链接性变量
 3 int a = 10;
 4 int b = 20;
 5 
 6 
 7 ///////////////////////          B.cpp
 8 //定义外部链接性变量
 9 int a = 30;
10 int d = 40;
Error Code

  解决方式,变量声明为static或者extern即可,如下代码:

//////////////////    A.cpp

//定义外部链接性变量
int a = 10;
int b = 20;


//////////////////    A.cpp
//定义外部链接性变量
static int a = 30;	//加上static,此时a的作用域之局限在本文件B.cpp中使用,而在C.cpp不能使用,C.cpp中使用的是A.cpp中的a
int d = 40;



//////////////////    C.cpp
#include <iostream>
using namespace std;

//使用外部链接性的变量前必须先声明:在前面添加修饰符extern
extern int a;     //这里使用的a的外部变量是A.cpp中的a而不是B.cpp中的a。我们等下看运行结果就知道
extern int b;
//extern int c;
extern int d;

void printf_local_out_value()
{
	static int a = 23333;
	//输出外部链接型的变量a;
	cout << "outer value: a = " << ::a << endl;

	//输出本地定义的a
	cout << "local value: a = " << a << endl;
}

void change_outer_static_value()
{
	cout << "update outer value before: d = " << ::d << endl;

	//全局的静态外部链接型变量只有一份内存单元,因此在外部文件所有文件共用的是同一个内存单元中的变量,只要一个改变,则这个值就会改变
	::d = ::d + 2333;
	cout << "update outer value later: d = " << ::d << endl;
}


int main(void)
{
	cout << "A.h: a = " << a << endl;
	cout << "A.h: b = " << b << endl;
	//cout << "B.h: c = " << c << endl;
	cout << "B.h: d = " << d << endl << endl;;

	printf_local_out_value();

	change_outer_static_value();

	cout << endl;
	
	return 0;
}

(1)、此时我们可以看到a的值为10,因此在C.cpp中使用的是A.cpp中a的定义,而B.cpp中的变量a通过static声明将a变量的作用域局限在了B.cpp文件中为内部链接性

(2)、::作用域运算符,我们可以看到,外部变量和局部变量两个是相互独立的。在同一代码块下,如果不同过::作用域运算符的话,全局变量(外链接)将被局部变量覆盖

(3)、因为静态变量只有一块内存单元存储值,所有的使用该静态变量的都指向同一片内存单元,因此一旦修改值那么静态变量的值就会改变。

>>.内部链接性:

   内部链接性的静态变量,它的作用域只是局限在本文件中,而在同一工程的其他文件中是不能访问,因为相对于外部文件,内部链接性的静态变量是不可见的(但是外部链接性的变量却是可见的)。如下代码:

/////////////////         A.cpp

#include <iostream>

//定义外部链接性变量
int a = 10;
int b = 20;

static char* ch = "i love china!";
char* ch1 = "i love girl!";

//下面的test1()函数如果加上static那么这个函数作用域也是限于A.cpp;那么在C.cpp中将不能调用
void test1()
{
	std::cout << "A.cpp --> ch address: " << static_cast<void*>(&ch) << std::endl;
	std::cout << "A.cpp -->ch1 address: " << static_cast<void*>(&ch1) << std::endl;
}



/////////////////         C.cpp
#include <iostream>
using namespace std;

extern char* ch1;
static char* ch = "i love program!";
extern void test1();

int main(void)
{
	
	cout << "C.cpp -->ch1 address: " << static_cast<void*>(&ch1) << endl;       //此处ch1的地址将会和A.cpp中ch1的地址相同
	cout << "C.cpp --> ch address: " << static_cast<void*>(&ch) << endl << endl;//此处ch的地址将会和A.cpp中ch的地址不相同,因为这是两个独立的变量

	//此函数做个说明,静态变量中所有规则对函数的限制和对变量一样,这里能调用A.cpp中的test1是因为test1()函数是全局的外部链接性函数
	//如果test1()声明为static那么在这里调用则不行。
	test1();
	
	return 0;
}

 

  从图中可以看出,外部链接性的静态变量在其他文件中是可以共享的,而static声明的内部链接性变量只能在声明的文件中使用。下面在说下:static_cast<void*>(&ch1),这条语句表示的是取ch1变量的地址,这是因为C++的输出流<<将所有的字符指针当作字符串来处理,因此直接输出cout<<ch1;那么输出的是ch1的整个字符串,而不是指针。

>>.无链接性:

   无链接性的静态变量,是定义在代码块中或者函数体中的静态变量,且必须要有static关键词修饰。详见下面代码:

#include <iostream>
using namespace std;

void non_link_static_value_test()
{
	static char* non_link = "i love program!";
	cout << "non_link_static_value_test() --> non_link address: " << static_cast<void*>(&non_link) << "   value:  " << non_link << endl;
}

int main(void)
{
	static char* non_link = "i love girl!";
	cout << "main()_1 --> non_link address: " << static_cast<void*>(&non_link) << "   value:  " << non_link << endl;
	non_link_static_value_test();

	{
		static char* non_link = "i love china!";
		cout << "main()_2 --> non_link address: " << static_cast<void*>(&non_link) << "   value:  " << non_link << endl;
	}
	cout << "main()_3 --> non_link address: " << static_cast<void*>(&non_link) << "   value:  " << non_link << endl;

	return 0;
}

  从图中可以看出无链接性的静态变量的作用域和自动变量一样,各个函数中的重名变量相互独立,在同一个函数中有内外层重名变量的时候,内层的变量作用域只在内层中,而内层中会屏蔽外层的同名变量。但是注意静态变量和动态变量是不同的,动态变量的内存在使用完之后就会自动释放,而静态变量不会,静态变量的生命周期是整个程序运行期间,在程序结束时才会自动释放。(适用于三种类型的静态变量)

>>.静态变量的生命周期测试:

#include <iostream>
using namespace std;

int static_value_life_times_test()
{
	static int test_value = 10;            //如果此变量的值为20,30,40的话就说明,静态变量在函数执行完之后,并不会立刻释放内存

	test_value += 10;

	return test_value;
}

int non_static_value_life_times_test()         //对比static变量
{
	int test_value = 10;

	test_value += 10;

	return test_value;
}

int main(void)
{
	cout << "    static_value_life_times_test: ";
	for (int i = 0; i < 3; i++)
	{
		cout << static_value_life_times_test() << ", ";
	}
	cout << endl << endl << "non_static_value_life_times_test: ";

	for (int i = 0; i < 3; i++)
	{
		cout << non_static_value_life_times_test() << ", ";
	}
	cout << endl;

	return 0;
}

  如图:我们可以看出,静态变量的值为: 20、30、40,这说明静态变量在每次函数执行完之后并没有释放内存,其只初始化了一次(如果重复初始化的话,结果就会和自动变量一样);而自动变量的值为: 20、 20、20;这说明自动变量则是在每次在执行完之后,释放内存,当再次调用的时候再次初始化和重新计算。

  就说到这里为止,其实想要验证某些书本上理论性的东西,我们在自己想不通的时候,可以编程来找到答案,这样可能会更加的直观。

 ---------------如有错误,希望大家多多指正---------------- 

原文地址:https://www.cnblogs.com/geore/p/5804759.html