变量的时空限制
变量作用域(空间限制)
变量作用域描述了此变量名在文件(翻译单元)的多大范围可见。变量的作用域与程序块(block)有关。
例如,在函数中定义的变量可以在该函数中使用,但不能在其他函数中使用;而在文件中的函数定义之前定义的变量则可在所有函数中使用。
局部变量和全局变量
我们可简单将定义的变量分为两类: 局部变量和全局变量 。
局部变量(Local Variable)
在程序块内(函数、复合语句)中定义的变量
main()函数中定义的变量也是局部变量(main函数也是一个函数,只不过对于运行环境来说特殊一些)
全局变量(Global Variable )
在所有函数之外定义的变量
作用范围:从定义位置到文件结束
作用:方便函数间的数据传递
如在作用范围外的函数要使用此变量,用关键词extern在函数内说明此全局变量。(比如要在另一个文件中引用此变量)
//file 作用域示例.c
void Function();
int x = 1; int y = 2; //全局变量
extern int z = 3; //可被外部文件链接的全局变量
int main()
{ int i=0; //作用域为main()的局部变量
cout <<x << “, ”<<y<<endl;
Function();
return 0;
}
void Function()
{ int a = 0; //作用域为Function()的局部变量
cout <<x << “, ”<<y<<endl;
}
规则
变量作用域遵循以下几个规则:
在程序块(block)中说明的变量是局部的,仅能在本块中和内部的块中存取。
如下程序中的
i
作用域只在for
循环内(for
是一个程序块而非函数),当跳出for
循环后,i
变量就无法被外部的main()
读取到。int main() { int sum = 0, m; // sum,m作用域 for (int i=0; i<5; i++) // i作用域 | { // | | cout<< "input a nmuber:"; // | | cin>>m; // | | sum = sum + m; // | | } // i作用域 | cout << "sum = "<< sum << endl; // | return 0; // sum, m作用域 }
当内部块与外部块有同名变量时,在内部块中屏蔽外部块的同名变量。(简单来说,就近原则,看最近的定义)
当局部变量与全局变量同名时,会按照就近原则选取变量,如
main()
函数中没有定义x,y
变量,则选取全局定义的x,y
变量值。而Function()
函数中重新定义了局部变量x,y
,因此在Function()
函数中就直接用定义局部变量的值。可见下程序运行结果。
void Function(); int x = 1; int y = 2; int main() { cout <<"x="<<x<<" y=" <<y<<endl; Function(); return 0; } void Function() { int x=2, y=1 ; cout <<"x="<<x<<" y=" <<y<<endl; } //output //x=1 y=2 //x=2 y=1
如果目前的程序块中有局部变量与程序的全局变量重名,那么如何调用全局变量?使用作用域限定符
::
// C++ program to show that we can access a global // variable using scope resolution operator :: when // there is a local variable with same name #include<iostream> using namespace std; // Global x int x = 0; int main() { // Local x int x = 10; cout << "Value of global x is " << ::x; cout<< "\nValue of local x is " << x; return 0; }
当实际参数与形式参数同名时,可见下程序运行结果。形参值改变不影响与其同名的实参值
void Function(int x, int y) ; int main() { int x = 1; int y = 2; cout <<"x="<<x<<" y=" <<y<<endl; Function(x,y); return 0; } void Function(int x, int y) { x=2; y=1 ; cout <<"x="<<x<<" y=" <<y<<endl; } //output //x=1 y=2 //x=2 y=1
注意形式参数不可以和局部变量同名。也就是在函数内不能定义与形式变量同名的变量。
在一个函数中,我们不能存取主调函数(caller)的变量,即使知道该变量的名字。
比如函数A调用了函数B,那在函数B中也无法调用函数A中定义的变量(即使知道该变量的名字)。
以上总结一下:
在程序块(block)中说明的变量是局部的,仅能在本块中和内部的块中存取。
当内部块与外部块有同名变量时,在内部块中屏蔽外部块的同名变量。
在一个函数中,我们不能存取主调函数(caller)的变量,即使知道该变量的名字。
全局变量使用须知
一般使用的情况
当多个函数必须共享同一个变量时
当少数几个函数必须共享大量数据时;
全局变量破坏了模块化,建议尽量少使用!
当全局变量和局部变量同名时,在局部变量的作用范围中全局变量被屏蔽。如果要使用全局变量,可以使用作用域运算符
::
。全局变量的使用将在模块化设计中详细介绍。
变量生存时限(时间限制)
变量的生存时限主要涉及到static
关键字的使用。也就是静态(static
)变量
我们知道一般在调用函数时,被调用函数内部的变量都是重新初始化的,这保证了每一次调用函数都能得到相同的结果,使函数变得通用(这也是我们定义函数的意义)。这样被调用函数中的变量生存时间就很短,在每次被调用时初始化,调用结束时被回收。
如下程序中的count
变量,在main()
函数每次调用demo()
时重新定义初始化,并在调用结束后被回收。这样的过程总共持续了5次。因此我们可以认为在程序执行过程中出现了五个count
变量(或许称为count1
count2
count3
count4
count5
)。它们的行为都是相同的。
#include <iostream>
#include <string>
using namespace std;
void demo()
{
int count = 0;
cout << count << " ";
count++;
}
int main()
{
for (int i=0; i<5; i++)
demo();
return 0;
}
//output
//0 0 0 0 0
但有时我们需要函数中的变量在整个运行程序中都持续存在,比如要记录调用了demo()
函数的次数,此时我们就要延长count
变量的生存时间,不让其在demo()
被调用完后就被回收。
static
关键字就起到这个作用,在修饰变量的时候,static修饰的静态局部变量只执行初始化一次,而且延长了局部变量的生命周期,直到程序运行结束以后才释放,且不改变作用域。如下程序
// C++ program to demonstrate
// the use of static Static
// variables in a Function
#include <iostream>
#include <string>
using namespace std;
void demo()
{
// static variable
static int count = 0;
cout << count << " ";
// value is updated and
// will be carried to next
// function calls
count++;
}
int main()
{
for (int i=0; i<5; i++)
demo();
return 0;
}
//output
//0 1 2 3 4
静态变量使用须知
未被程序员初始化的静态变量都由系统初始化为0
局部静态变量的初值是编译时赋的。当运行时重复调用此函数时,不重复赋初值。
虽然局部静态变量在函数调用结束后仍然存在,但其他函数不能引用它
局部的静态变量在程序执行结束时消亡
用
static
还可以用在函数定义或说明中。该函数只能被用于本源文件中,其他源文件不能调用此函数(达到了隐藏的作用)
C++内存布局(Memory Layout)
static
变量只是C++存储类别中的一种,因为存储类别的不同,定义的变量将会有不同的特性,如static variable
的主要特性就在前介绍了。
存储类别
在C++语言中 ,每个变量有两个属性:
数据类型:变量所存储的数据类型
存储类别:变量的存储位置和期限
标准的变量定义:
存储类别 数据类型 变量名
如static int count
存储类别主要有四类
自动变量
auto
静态变量
static
寄存器变量
register
外部变量
extern
内存布局
之所以称为存储类别,是因为在一个C++语言生成的可执行文件在内存中有不同的存储位置。
典型的可执行文件可分为两部分
代码段(
.text
),由编译后的机器指令组成,为只读数据段:
初始化的数据段(initialized data segment):也称
.data
段,需要与内存进行数据交互未初始化的数据段(uninitialized data segment):也称
.bss
( block stated by symbol )段,此段的变量只有名称和大小,没有值(故无需内存数据交互)栈(.
stack
):存储动态数据堆(
.heap
):动态内存布局(由C++中的new)
下面介绍各个不同存储类别的变量在内存不居中的位置及其特性
自动变量
auto
在函数内或块内定义的变量缺省时是
auto
。如auto int i
等价于int i
; 也就是说我们平时定义的变量都是自动变量,其有以下特性当函数被调用时,系统为自动变量分配空间(栈中以桢的形式保存)
当退出时,系统释放分配给自动变量的值
当再次进入该块时,系统重新分配空间
简单来说,就是
auto
变量存储在栈(.stack
)中,每次被调用时被分配到栈中,调用结束就出栈。*注意C++11后
auto
关键字被用于自动推断变量类型,自动变量无需再用auto
*说明静态变量
static
静态变量就是在整个程序运行期间都存在的变量,当程序开始运行时就加载到内存中,程序完全退出时一起释放内存。
存储位置:
.bss
或.data
区别在于其是否初始化。静态的局部变量:离开函数后,值仍保留
静态的全局变量:作用域为整个文件,就算有extern也无法跨文件访问,会触发linkage error(也就是作用域为此文件,其他文件无法引用)
寄存变量
register
代替自动变量或形参,可以提高变量的访问速度(因为处理器访问寄存器的速度是最快的)。
存储位置:寄存器
如无合适的寄存器可用,则编译器把它设为自动变量。
只有局部自动变量才能定义为寄存器变量,全局变量和静态局部变量不能定义。
外部变量
extern
声明一个不在本模块作用范围内的全局变量。主要用于多文件编译程序,目前大家都只用一个文件运行可能不需要考虑这个类型的变量,但在较大工程中的代码规范必然会用到
extern
变量。在某函数中引用了一个声明在本函数后的全局变量时,需要在函数内用extern声明此全局变量
当一个程序有多个源文件组成时,用extern可引用另一文件中的全局变量。