变量的存储分配与作用域

1.变量的数据类型和存储类型

C语言中,每一个变量都有两个属性:数据类型和存储类型。数据类型即常说的字符型、整型、浮点型;存储类型则指变量在内存中的存储方式,它决定了变量的作用域和生存期。

变量的存储类型有以下四种:auto(自动)、register(寄存器)、extern(外部)和static(静态)。其中autoregister用于声明内部变量,auto变量是存储在栈中的,register变量是存储在寄存器中的。static用于声明内部变量或外部变量,extern用于声明外部变量,它们是存储在静态存储区的。

变量声明的一般形式:<存储类型> <数据类型> <变量名列表>

当声明变量时未指定存储类型,则内部变量的存储类型默认为auto型,外部变量的存储类型默认为extern型。

外部变量有两种声明方式:定义性声明和引用性声明。

定义性声明是为了创建变量,即需为变量分配内存。引用性声明是为了建立变量与内存单元之间的关系,表示要引用的变量已在程序源文件中其他地方进行过定义性声明。定义性声明只能放在函数外部,而引用性声明可放在函数外部,也可放在函数内部。

extern int b;//引用性声明,也可放在函数fun中 void fun() { printf("d%",b);//输出 } extern int b=5;//定义性声明,可以省略关键字extern

2.变量的作用域

变量的作用域是指一个范围,是从代码空间的角度考虑问题,它决定了变量的可见性,说明

变量在程序的哪个区域可用,即程序中哪些行代码可以使用变量。作用域有三种:局部作用域、全局作用域、文件作用域,相对应于局部变量(local variable)、全局变量和静态变量(global variable)。

(1)局部变量

大部分变量具有局部作用域,它们声明在函数(包括main函数)内部,因此局部变量又称为内部变量。在语句块内部声明的变量仅在该语句块内部有效,也属于局部变量。局部变量的作用域开始于变量被声明的位置,并在标志该函数或块结束的右花括号处结束。函数的形参也具有局部作用域。

#include <iostream> using namespace std; int main() { int x = 0; { int x=1; cout << x << endl; { cout << x << endl; int x = 2; // "x = 1" lost its scope here covered by "x = 2" cout << x << endl; // x = 2 // local variable "x = 2" lost its scope here } cout << x << endl; // x = 1 // local variable "x = 1" lost its scope here } cout << x << endl; // local variable "x = 0" lost its scope here return 0; }

(2) 全局变量及extern关键字

以下是MSDNC/C++extern关键字的解释:

The extern Storage-Class SpecifierC

A variable declared with the extern storage-class specifier is a reference to a variable with the same name defined at the external level in any of the source files of the program. The internal extern declaration is used to make the external-level variable definition visible within the block. Unless otherwise declared at the external level, a variable declared with the extern keyword is visible only in the block in which it is declared.

The extern Storage-Class SpecifierC++

The extern keyword declares a variable or function and specifies that it has external linkage (its name is visible from files other than the one in which it's defined). When modifying a variable, extern specifies that the variable has static duration (it is allocated when the program begins and deallocated when the program ends). The variable or function may be defined in another source file, or later in the same file. Declarations of variables and functions at file scope are external by default.

全局变量声明在函数的外部,因此又称外部变量,其作用域一般从变量声明的位置起,在程序源文件结束处结束。全局变量作用范围最广,甚至可以作用于组成该程序的所有源文件。当将多个独立编译的源文件链接成一个程序时,在某个文件中声明的全局变量或函数,在其他相链接的文件中也可以使用它们,但是必须做extern引用性声明。

关键字extern为声明但不定义一个对象提供了一种方法。实际上,它类似于函数声明,承诺了该对象会在其他地方被定义:或者在此文本文件中的其他地方,或者在程序的其他文本文件中。

如果一个函数要被其他文件中函数使用,定义时加extern关键字,在没有加externstatic关键字时,一般有的编译器会默认是extern类型的,因此你在其他文件中可以调用此函数。因此,extern一般主要用来做引用性声明。

但是,有些编译器以及在一些大型项目里,使用时一般的会将函数的定义放在源文件中不加extern,而将函数的声明放在头文件中,并且显式的声明成extern类型,需要使用此函数的源文件只要包含此头文件即可。

在使用extern 声明全局变量或函数时,一定要注意:所声明的变量或函数必须在且仅在一个源文件中实现定义。如果你的程序声明了一个外部变量,但却没有在任何源文件中定义它,程序将可以通编译,但无法链接通过:因为extern声明不会引起内存被分配!

在线程存在的情况下,必须做特殊的编码,以便同步各个线程对于全局对象的读和写操作。

另外,extern也可用来进行链接指定。C++中的extern "C"声明是为了实现C++C及其它语言的混合编程,其中被extern "C"修饰的变量和函数是按照C语言方式编译和链接的。

如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern "C" { }

In C++, when used with a string, extern specifies that the linkage conventions of another language are being used for the declarator(s). C functions and data can be accessed only if they are previously declared as having C linkage. However, they must be defined in a separately compiled translation unit.

Microsoft C++ supports the strings "C" and "C++" in the string-literal field. All of the standard include files use the extern "C" syntax to allow the run-time library functions to be used in C++ programs.

例如,在C++工程中要包含C语言头文件,则一般这样:

extern "C" { #include <stdio.h> }

示例工程testExtern包含三个文件:C.cCPP.cpptestExtern.cpp

// C.c #include <stdio.h> int intC = 2010; void funC() { printf("funC()/n"); } // CPP.cpp #include <stdio.h> extern int global; /*extern*/ int intCPP = 2011; /*extern*/ const char* str = "defined outside"; /*extern*/ int intArray[3] = {2012, 2013, 2014}; static int staticIntCPP = 2015; void funCPP() { printf("funCPP() - localStatic : %d, globalExtern : %d/n", staticIntCPP, global); } // testExtern.cpp #include <stdio.h> /*extern*/ int global = 2016; extern "C" void funC(); // C.c中实现 /*extern*/ void funCPP(); // CPP.cpp中实现,函数的声明默认在前面添加了extern:因为此处只声明,肯定在其他地方实现的。 // 以下代码按C方式编译链接 extern "C" void funC1() { printf("funC1()/n"); } extern "C" { void funC2() { printf("funC2()/n"); } } extern "C" void funC3(); // 本文件中其他地方(或外部文件)实现,按照C方式编译链接 /*extern*/ void fun(); // 本文件中其他地方(或外部文件)实现 extern "C" int intC; // C linkage specification in C++ must be at global scope int main() { printf("intC = %d/n", intC); extern int intCPP; // 或者放在main之前。如果去掉extern就变成了main()内部定义的局部变量! printf("intCPP = %d/n", intCPP); extern const char* str; // 或者放在main之前。 printf("str = %s/n", str); extern int intArray[]; for (int i = 0; i < 3; i++) { printf("intArray[i] = %d/n", intArray[i]); } // extern int staticIntCPP; // error LNK2001 // printf("staticIntCPP = %d/n", staticIntCPP); funC(); funCPP(); funC1(); funC2(); funC3(); fun(); return 0; } void funC3() { printf("funC3()/n"); } void fun() { printf("fun()/n"); }

(3) 静态变量及static关键字

文件作用域是指在函数外部声明的变量只在当前文件范围内(包括该文件内所有定义的函数)可用,但不能被其他文件中的函数访问。一般在具有文件作用域的变量或函数的声明前加上static修饰符。

static静态变量可以是全局变量,也可以是局部变量,但都具有全局的生存周期,即生命周期从程序启动到程序结束时才终止。

#include <stdio.h> void fun() { static int a=5;//静态变量a是局部变量,但具有全局的生存期 a++; printf("a=%d/n",a); } int main() { int i; for(i=0;i<2;i++) fun(); getchar(); return 0; }

输出结果为:

a=6

a=7

static操作符后面生命的变量其生命周期是全局的,而且其定义语句即static int a=5;只运行一次,因此之后再调用fun()时,该语句不运行。所以f的值保留上次计算所得,因此是6,7.

以下initWinsock例程中借助局部静态变量_haveInitializedWinsock保证Winsock只初始化一次。

int initWinsock(void) { static int _haveInitializedWinsock = 0; WORD WinsockVer1 = MAKEWORD(1, 1); WORD WinsockVer2 = MAKEWORD(2, 2); WSADATA wsadata; if (!_haveInitializedWinsock) { if (WSAStartup(WinsockVer1, &wsadata) && WSAStartup(WinsockVer2, &wsadata)) { return 0; /* error in initialization */ } if ((wsadata.wVersion != WinsockVer1) && (wsadata.wVersion != WinsockVer2)) { WSACleanup(); return 0; /* desired Winsock version was not available */ } _haveInitializedWinsock = 1; } return 1; }

同一个源程序文件中的函数之间是可以互相调用的,不同源程序文件中的函数之间也是可以互相调用的,根据需要我们也可以指定函数不能被其他文件调用。根据函数能否被其他源程序文件调用,将函数分为内部函数和外部函数。

如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型的前面加static

内部函数又称静态函数。使用内部函数,可以使函数只局限于所在文件,如果在不同的文件中有同名的内部函数,互不干扰。

通常把只能由同一文件使用的函数和外部变量放在一个文件中,在它们前面都冠以static使之局部化,其他文件不能引用。

由于静态变量或静态函数只在当前文件(定义它的文件)中有效,所以我们完全可以在多个文件中,定义两个或多个同名的静态变量或函数。这样当将多个独立编译的源文件链接成一个程序时,static修饰符避免一个文件中的外部变量由于与其他文件中的变量同名而发生冲突。

比如在A文件和B文件中分别定义两个静态变量a

A文件中:static int a;

B文件中:static int a;

这两个变量完全独立,之间没有任何关系,占用各自的内存地址。你在A文件中改a的值,不会影响B文件中那个a的值。

参考:

《程序设计与C语言》 梁力

《白话C++ 南郁

Visual C++面向对象编程教程》 王育坚

C++ Primer Stanley B. Lippman

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值