标识符(identifier)是程序中变量、类型、函数和标号的名称,它可以由程序设计者命名,也可以由系统指定。标识符由字母、数字和下划线组成,第一个字符不能是数字。 通常来说,一段程序代码中所用到的标识符(名字)并不总是有效可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域(scope)。 作用域的使用能够提高程序逻辑的局部性,增强程序的可靠性,减少名字冲突。 对于C来说,符号{}可以界定标识符的scope,关键字extern、static也可以拓宽或限制标识符的作用域。同时,类名、命名空间也可以限制标识符的作用域。1globalscope(全局作用域) 典型的具有全局作用域的名字,包括:全局变量、全局函数等。 Useofglobalvariablesisgenerallydiscouraged。Itmakesyourprogrammoredifficulttounderstand,andhardertodebug。Butsometimesusingaglobalvariableisacceptable。 通常不鼓励使用全局变量。它使您的程序更难理解,也更难调试。但有时使用全局变量是可以接受的。global。hifndefGLOBALDOTHThisisanincludeguarddefineGLOBALDOTHThistellsthecompilerthatgmyglobalexistssomewhere。Withoutextern,thiswouldcreateanewvariablenamedgmyglobalineveryfilethatincludedit。Dontmissthis!Declaregmyglobal,thatispromiseitwillbedefinedbysomemodule。typedefenum{RESOURCEUNDEFINED1,RESOURCEUNKNOWN0,RESOURCEOK,RESOURCECANCEL,RESOURCEABORT,Insertmorehere。RESOURCEMAXThemaximumnumberofresourcesdefined。}EnumResourceID;externconstcharconstresources〔RESOURCEMAX〕;externconstintgcvar12;endifGLOBALDOTHglobal。cincludeglobal。hAlwaysincludetheheaderfilethatdeclaressomethingintheCfilethatdefinesit。Thismakessurethatthedeclarationanddefinitionarealwaysinsync。Definemyglobal。Aslivinginglobalscopeitgetsinitialisedto0onprogramstartup。全局变量(外部变量),在加载阶段加载constcharconstresources〔RESOURCEMAX〕{unknown,OK,Cancel,Abort};main。cincludeglobal。hvoidtest(){}全局函数intmain(void){gmyglobal42;EnumResourceIDresourceidRESOURCEUNDEFINED;while((resourceid)RESOURCEMAX){GoalKicker。comCNotesforProfessionals252printf(resourceID:d,resource:s,resourceid,resources〔resourceid〕);}return0;}2FunctionPrototypeScope(函数原型作用域) 函数声明也称为函数原型。includestdio。hTheparametername,apple,hasfunctionprototypescope。Thesenamesarenotsignificantoutsidetheprototypeitself。Thisisdemonstratedbelow。inttestfunction(intapple);FunctionPrototypeScopeintmain(void){intorange5;orangetestfunction(orange);printf(dr,orange);orange6return0;}inttestfunction(intfruit){fruit1;}3Functionscope(函数作用域) Functionscopeisthespecialscopeforlabels。Thisisduetotheirunusualproperty。Alabelisvisiblethroughtheentirefunctionitisdefinedandonecanjump(usinginstructiongotolabel)toitfromanypointinthesamefunction。 函数作用域是标号的特殊范围。这是因为它们的特殊属性。标号在定义的整个函数中都是可见的,可以从同一函数中的任意点跳转(使用指令gotolabel)到它。includestdlib。hincludestdio。hvoidclose(FILEfp){}voidafunction(void){doublea(double)malloc(sizeof(double〔34〕));if(!a){fprintf(stderr,cantallocate);Nopointinfreeingaifitisnull}FILEbfopen(somefile,r);if(!b){fprintf(stderr,cantopen);gotoCLEANUP1;Fnopointinclosingb}dosomethingreasonableif(error){fprintf(stderr,somethingswrong);gotoCLEANUP2;Freeaandclosebtopreventleaks}doyetsomethingelseCLEANUP2:close(b);CLEANUP1:free(a);}main(){} LabelssuchasCLEANUP1andCLEANUP2arespecialidentifiersthatbehavedifferentlyfromallotheridentifiers。Theyarevisiblefromeverywhereinsidethefunction,eveninplacesthatareexecutedbeforethelabeledstatement,oreveninplacesthatcouldneverbereachedifnoneofthegotoisexecuted。Labelsareoftenwritteninlowercaseratherthanuppercase。 CLEANUP1和CLEANUP2等标号是特殊标识符,其行为与所有其他标识符不同。它们在函数内部的任何地方都是可见的,甚至在标记语句之前执行的地方,或者如果没有执行goto,就永远无法到达的地方。标号通常用小写字母而不是大写字母书写。4BlockScope(块作用域) Anidentifierhasblockscopeifitscorrespondingdeclarationappearsinsideablock(parameterdeclarationinfunctiondefinitionapply)。Thescopeendsattheendofthecorrespondingblock。 如果标识符的相应声明出现在块内,则该标识符具有块作用域(函数定义中的参数声明适用)。作用域在相应块的末尾结束。 Nodifferententitieswiththesameidentifiercanhavethesamescope,butscopesmayoverlap。Incaseofoverlappingscopestheonlyvisibleoneistheonedeclaredintheinnermostscope。 具有相同标识符的不同实体不能具有相同的作用域,但作用域可能重叠。对于重叠的作用域,唯一可见的是在最内层作用域中声明的作用域。includestdio。hvoidtest(intbar)barhasscopetestfunctionblock{intfoo5;foohasscopetestfunctionblock{intbar10;barhasscopeinnerblock,thisoverlapswithprevioustest:bardeclaration,andithidestest:barprintf(dd,foo,bar);510}endofscopeforinnerbarprintf(dd,foo,bar);55,herebaristest:bar}endofscopefortest:fooandtest:barintmain(void){intfoo3;foohasscopemainfunctionblockprintf(d,foo);3test(5);printf(d,foo);3return0;}endofscopeformain:foo5FileScope(文件作用域)includestdio。hTheidentifier,foo,isdeclaredoutsideallblocks。Itcanbeusedanywhereafterthedeclarationuntiltheendofthetranslationunit。variablefilescopestaticcvoidtestfunction(void)functionfilescope{foo2;}constintcvar12;默认为文件作用域,当用exetern修饰且有初始化时,为全局作用域intmain(void){foo1;testfunction();printf(dr,foo);foo3;return0;}6namespacescope(命名空间作用域) 命名空间的成员名字具有命名空间作用域(namespacescope)。该名字的作用域从在该命名空间内的声明点直至当前编译单元内该命名空间的结尾处。使用usingnamespace语句可以提升该命名空间此时已经声明的名字到该using语句的作用域。 使用限定符::,可以在命名空间名字的作用域内访问该命名空间的成员名字。 C引入了无名命名空间(unnamednamespace),其作用域即为当前编译单元。例如: namespace{} 也可以通过前面加上::限定访问文件作用域中的名字。7classscope(类作用域) 类(class、struct、union)内定义的名字的作用域称为类作用域(classscope)。这些名字在当前类的定义内部,以及类定义词法范围外的类成员定义中是可见的。因此,在类内部,成员名字可以先使用后定义,不必前向声明(forwarddeclaration)。 类静态数据成员具有外部链接属性。 类的成员名字在其所在的类作用域内、或者派生类作用域内可见,或者通过。运算符、运算符、::限定符访问。 匿名类的作用域,这里的匿名类是指匿名struct、匿名class、匿名union,且没有直接用这种类型定义变量。如果紧随这些无名类型的定义之后,定义了该类型的变量,则类型的定义及使用与普通情况完全一样;严格说,这种情形可以不算是匿名类。 匿名类作为嵌套类,即匿名类在一个外部类的内部定义:则编译器就在此处定义一个该匿名类的无名变量,并把该匿名类的成员的名字提升到该类型定义所在的外部类的作用域内。由于匿名类不能使用点运算符访问其成员,所以匿名类只能有数据成员,不允许有成员函数,也不能包含私有或受保护的数据成员。如果匿名类的定义是连续嵌套,则最内部的匿名类的成员名字被提升至最外部的非匿名类或可用变量访问的成员类之处。 匿名类不作为嵌套类定义,即匿名类定义在一个全局函数内部或者全局函数外部。CC语言标准只允许匿名union在这种情形下定义;如果是匿名struct、匿名class,则编译报错。对于此种情形的匿名union,编译器同样在此处定义一个该匿名联合的无名变量,并把该匿名联合的成员的名字提升到该匿名联合所在的作用域内,匿名联合只能有数据成员,不允许有成员函数,也不能包含私有或受保护的数据成员。在函数外的匿名联合只能在当前编译单元内可见,因此必须使用static关键字,或者必须放在匿名命名空间中。 例如:intmain(){union{};test5;匿名union的成员的名字提升到定义了该匿名union的作用域内。struct{}v;匿名struct,但是紧随其后声明了一个变量vv。i101;编译通过i102;编译报错:iwasnotdeclaredinthisscopereturn0;}8enumerationscope(枚举作用域) 枚举作用域是指枚举类型的成员(enumerator)的名字的作用域,起自其声明之处,终至枚举定义结束之处。 C语言规定,枚举类型的成员(enumerator)的可见范围被提升至该枚举类型所在的作用域内。这被认为有可能污染了外部的作用域。为此,C11引入了枚举类(enumclass)解决此问题。 End