程序使用标识符做为变量名,变量的属性包括名字、类型、大小和值。同样的,自定义函数的名字也需要使用标识符做为名字。实际上,程序中的每个标识符还有其他的属性,包括存储类别、作用域和连接。
变量名字:由编译器建立标识符与分配的存储空间首个“存储位”地址的对应关系;
类型、大小:变量名字只是对应存储单元的首个“存储位”的地址,一般来说,除了bool型和字符型,其它如整数型、浮点型都需要多个“存储位”来保存,而多少个“存储位”则由类型和大小来决定;
变量值:存储空间所对应的内容;
存储类别:标识符的存储类别决定了标识符在内存中存在的时间。有些标识符存在的时间较短,有些标识符可以重复地创建和销毁,还有些标识符在整个程序执行过程中一直存在。
连接:标识符的连接决定了标识符是只有在声明它的源文件中可以识别,还是在经编译然后连接在一起的多个文件中可以识别。标识符的存储类别说明符用于确定存储类别和连接。
作用域:标识符的作用域是指标识符在程序中可以被引用的范围(可以使用的地方)。有些标识符在整个程序中都能被引用,有些只能限于在程序的某个部分引用。
块作用域:从声明开始到标识符所在语句的结束右花括号“}”处,如函数参数、局部变量;
文件作用域:一般指全局变量和函数,即在整个文件中从定义的起始的位置后都能被访问,位于函数之外的全局变量、函数定义和函数原型都具有文件作用域;
原型作用域:即形参的作用域,一般就是在整个函数代码块中有效。
函数作用域:形如“start:”的标签,和goto有关,可以在函数体中使用。
类作用域:类的数据成员和成员函数属于该类的作用域。非成员函数在文件作用域中定义。在类的作用域内,类的成员可以被类的所有成员函数直接访问。在类的作用域之外,public类成员通过对象的句柄(对象名称、引用或指针)引用。
名字空间作用域:为避免标识符重叠而使用命名空间进行限制,如MyNameSpace::member,就像文件夹与文件名的关系;
标识符存储类别的生存期和定义或声明位置的形成的作用域
关键字 | 定义位置 | 存储区 | 生存期 | 作用域类别 | 作用域 |
auto | 函数块、类中 | 栈区 | 函数调用执行期间 | 局部变量 | 所定义函数内部 |
auto | 函数块、类外 | 静态区 | 程序运行的整个过程 | 全局变量 | 所定义之处到本源文件结束 |
register | 函数块、类中 | CPU的寄存器中 | 函数调用执行期间 | 局部变量 | 所定义函数内部 |
static | 函数块、类中 | 静态区 | 程序运行的整个过程 | 局部变量 | 所定义函数内部 |
static | 函数块、类外 | 静态区 | 程序运行的整个过程 | 全局变量 | 所定义之处到本源文件结束 |
extern | 函数块、类中 | 静态区 | 所定义之处到本源文件结束 | 外部变量 | 能链接在一起的全部文件 |
extern | 函数块、类外 | 静态区 | 所定义之处到本源文件结束 | 外部变量 | 能链接在一起的全部文件 |
register:建议编译器在计算机的高速硬件寄存器中而不是在内存中保存此变量。如果没有足够数量的寄存器供编译器使用,编译器会忽略掉rigister的声明。(程序的机器语言版本中的数据一般都是加载到寄存器中进行计算和其他处理的)注意不能用全局变量去申请,一般默认为auto,这和全局变量的生命周期有关。auto:只有函数中的局部变量可以是自动存储类别。在程序执行到定义它们的程序块时创建,在程序块激活时存在,在程序块执行完后销毁。默认情况下局部变量是自动存储类别的。自动存储是节省内存的一个表现。自动存储是一个最小特权原则的例子,在不需要时也就不需要再保存在内存中。
static:用于给静态存储类别的变量和函数声明标识符,静态变量的初值是在编译时就进行初始化了且整个程序运行期间只初始化一次;用于修饰局部变量时修改的是其存储类型,使其成为静态存储,但是作用域不改变。全局变量本身具有外部链接特性,如果我们希望该外部变量只能在本文件内使用,而不能被其他文件引用可以在外部变量定义时加static声明即改变了该全局变量的链接属性,防止别人写的模块误用。
extern:全局变量具有外部链接特性,如果要引用同一源程序中其它文件中定义的全局变量(静态全局变量除外),需要在前面加上extern,编译时遇到extern,会先在文件内找是否定义了该外部变量。如果未找到则在链接时在其他文件中找。extern只是说明那个变量在别的地方定义了,和存储类型的概念不同。当然,extern也可以在函数内部引用本文件中定义的全局变量,而忽略函数内相同的局部变量。
备注
存储类别和作用域是两个独立的问题。在整个程序运行期间都一直存在的变量或函数并不表示其可以被源文件中位于其声明或者定义之后的任何函数引用。
全局变量不允许声明为auto变量,register不适用于全局变量只能是局部自动变量和形参。
register是不能取址的。声明变量为register,编译器并不一定会将它处理为寄存器变量,毕竟,寄存器资源是有限的。
auto、register是用来修饰变量的,static、extern修饰变量、函数都可以。
Static局部变量时在编译赋初值,自动变量的初值是函数调用时赋的。
extern声明变量为全局,static声明并定义变量为全局/局部静态。普通变量定义在全局则为全局变量,普通变量定义在函数内则为局部变量。
全局变量在本文件有效,其他文件可引用。
可以通过一个实例进行说明:
// A scoping example.
#include <iostream>
#include <conio.h>
using std::cout;
using std::endl;
void useLocal( void ); // function prototype
void useStaticLocal( void ); // function prototype
void useGlobal( void ); // function prototype
int x = 1; // global variable
int main()
{
int x = 5; // local variable to main
cout << "local x in main's outer scope is " << x << endl;
{ // start new scope
int x = 7; // hides x in outer scope
cout << "local x in main's inner scope is " << x << endl;
} // end new scope
cout << "local x in main's outer scope is " << x << endl;
useLocal(); // useLocal has local x
useStaticLocal(); // useStaticLocal has static local x
useGlobal(); // useGlobal uses global x
useLocal(); // useLocal reinitializes its local x
useStaticLocal(); // static local x retains its prior value
useGlobal(); // global x also retains its value
cout << "\nlocal x in main is " << x << endl;
getch();
return 0; // indicates successful termination
} // end main
// useLocal reinitializes local variable x during each call
void useLocal( void )
{
int x = 25; // initialized each time useLocal is called
cout << "\nlocal x is " << x << " on entering useLocal" << endl;
x++;
cout << "local x is " << x << " on exiting useLocal" << endl;
} // end function useLocal
// useStaticLocal initializes static local variable x only the
// first time the function is called; value of x is saved
// between calls to this function
void useStaticLocal( void )
{
static int x = 50; // initialized first time useStaticLocal is called
cout << "\nlocal static x is " << x << " on entering useStaticLocal"
<< endl;
x++;
cout << "local static x is " << x << " on exiting useStaticLocal"
<< endl;
} // end function useStaticLocal
// useGlobal modifies global variable x during each call
void useGlobal( void )
{
cout << "\nglobal x is " << x << " on entering useGlobal" << endl;
x *= 10;
cout << "global x is " << x << " on exiting useGlobal" << endl;
} // end function useGlobal
/**************************************************************************
* (C) Copyright 1992-2005 by Deitel & Associates, Inc. and *
* Pearson Education, Inc. All Rights Reserved. *
* *
* DISCLAIMER: The authors and publisher of this book have used their *
* best efforts in preparing the book. These efforts include the *
* development, research, and testing of the theories and programs *
* to determine their effectiveness. The authors and publisher make *
* no warranty of any kind, expressed or implied, with regard to these *
* programs or to the documentation contained in these books. The authors *
* and publisher shall not be liable in any event for incidental or *
* consequential damages in connection with, or arising out of, the *
* furnishing, performance, or use of these programs. *
**************************************************************************/
运行结果:
local x in main's outer scope is 5
local x in main's inner scope is 7
local x in main's outer scope is 5
local x is 25 on entering useLocal
local x is 26 on exiting useLocal
local static x is 50 on entering useStaticLocal
local static x is 51 on exiting useStaticLocal
global x is 1 on entering useGlobal
global x is 10 on exiting useGlobal
local x is 25 on entering useLocal
local x is 26 on exiting useLocal
local static x is 51 on entering useStaticLocal
local static x is 52 on exiting useStaticLocal
global x is 10 on entering useGlobal
global x is 100 on exiting useGlobal
local x in main is 5
程序中,在数据声明中定义的变量和数组,它们存储在静态存储区或者动态存储区,其空间由系统负责分配,最终也由系统负责回收,分配时机和回收时机用户无法控制。另外,它们都有自己的名字(因为定义时有命名),程序中可以通过变量名或数组名访问它们。
而“动态分配”的变量或数组,分配时机和释放时机可完全由程序员自己决定。另外,“动态分配”的空间,由于没有数据分明,因此这部分空间没有名字,无法像使用变量或数组那样通过变更名或数组名引用其中的数据,只能通过指针变量来访问。
对于数据在内存中的存储,C语言提供了3种策略,即静态分配、自动分配(栈式分配)和动态分配。
1 静态分配:静态分配由编译器自动分配和释放,在编译时就确定了运行时变量在内存中的位置。这种性质允许变量在整个程序的运行过程中都不消失。在C语言中使用关键字static来定义静态变量(可以是静态局部变量、静态全局变量或者在所有函数外面定义的全局变量)。
静态存储区是指存储单元在程序运行的整个过程中一直分配给某些变量,这些变量一直“静静地”占有这些单元,一直都不释放,直到程序运行结束。
2 自动内存分配(在栈上分配):自动分配同样也是由编译器自动分配和释放,在编译时并不确定变量在内存中的位置,而是在调用函数时给变量分配空间,函数返回时释放空间。C编译器通过系统栈来管理这样的变量。在C语言中,auto型变量是通过栈来管理的,不需要人为管理。
动态存储区是指存储单元在程序运行的不同时间可以分配不同的数据。需要时开辟单元,不需要时就释放该单元。这样某单元就可以在不同的时间分配给不同的变量,如函数里auto类型的局部变量就是这样,调用函数时,分配变量空间,退出函数时释放该空间。再调用时,再开辟...
3 动态内存分配(在堆上分配):动态分配是指在程序运行过程中动态申请内存的方法。这样的内存空间是从一个叫做堆的空间申请的,申请的空间是一段连续的内存空间。不需要该内存空间时,应该人为释放它,否则会导致内存资源的浪费。
内存中有一块称为“堆”(heap)的存储区,程序可以根据需要随时用stdlib.h提供的malloc()等函数在堆中申请一部分空间来存储数据,当不需要时又可以随时用free()函数释放。这种根据需要随时开辟、随时释放的内存分配方式,称为动态内存分配。
C语言中malloc()大体的实现是,从操作系统一次性地取得比较大的内存,然后将这些内存“零售”给应用程序。
关键字 | 定义位置 | 存储区 | 生存期 | 作用域类别 | 作用域 |
auto | 函数块、类中 | 栈区 | 函数调用执行期间 | 局部变量 | 所定义函数内部 |
auto | 函数块、类外 | 静态区 | 程序运行的整个过程 | 全局变量 | 所定义之处到本源文件结束 |
register | 函数块、类中 | CPU的寄存器中 | 函数调用执行期间 | 局部变量 | 所定义函数内部 |
static | 函数块、类中 | 静态区 | 程序运行的整个过程 | 局部变量 | 所定义函数内部 |
static | 函数块、类外 | 静态区 | 程序运行的整个过程 | 全局变量 | 所定义之处到本源文件结束 |
extern | 函数块、类中 | 静态区 | 所定义之处到本源文件结束 | 外部变量 | 能链接在一起的全部文件 |
extern | 函数块、类外 | 静态区 | 所定义之处到本源文件结束 | 外部变量 | 能链接在一起的全部文件 |
内存区 | 存储的内容 | |
代码区 | 程序代码 | 程序运行后程序代码保存到代码区。 |
常量区 | 常量数据 | 编译完成后到程序结束都存在,可以访问但是不能被修改。 |
静态存储区 | 全局变量、静态局部变量 | 编译阶段初始化,且只赋值一次,对静态局部变量多次调用时,每次在前一次的结果上进行。 |
动态存储区(栈区) | 局部动态变量、形参 | 在进入它们的函数或复合语句时才初始化,相当于赋值语句。每调用一次,就重新赋值一次。 |
动态分配区(栈区) | 动态数据 | 程序运行时与用户交互完成。开发者通过在C语言中通过函数malloc()、在C++通过new标识符申请时存在,在通过对应的free()或del申请释放时销毁; |