基本介紹
- 中文名:外部名作用域
- 外文名:scope of external name
- 學科:計算機科學
- 命名:遵從程式語言或系統的命名準則
- 套用:程式或系統
- 作用:共享、作用全域
簡介,命名規則,作用域的類別,
簡介
在計算機科學中,作用域(scope)是名字(name)與實體(entity)的綁定(binding)保持有效的那部分電腦程式。顯然,這種名字綁定既可以是在編譯時的靜態綁定,也可使程式運行時的動態綁定,所產生的作用域分別稱為靜態作用域與動態作用域。外部名作用域是相對內部名作用域而言的,例如在一個C程式中,外部名的作用域是整個程式,包括定義它的C源程式檔案和構成該程式的其他C源檔案,對於它都是可視的。
命名規則
TheC Programming Language書中第二章講到變數名時有這么一段話:
“對於內部名而言,至少前31個字元是有效的。函式名與外部變數名包含的字元數目可能小於31,這是因為彙編程式和載入程式可能會使用這些外部名,而語言本身是無法控制載入和彙編程式的。對於外部名,ANSI標準僅保證前6個字元的唯一性,並且不區分大小寫。”解釋如下:
A N S I標準規定,標識符可以為任意長度,但外部名必須至少能由前6個字元唯一地區分,並且不區分大小寫。這裡外部名指的是在連結過程中所涉及的標識符,其中包括檔案間共享的函式名和全局變數名。因此外部名abcdefgh和abcdef將被當作同一個標識符處理。
A N S I標準還規定內部名必須至少能由前31個字元唯一地區分。內部名指的是僅出現於定義該標識符的檔案中的那些標識符。C語言中的字母是有大小寫區別的,因此count Count COUNT是三個不同的標識符。標識符不能和C語言的關鍵字相同,也不能和用戶已編制的函式或C語言庫函式同名。
C語言標識符命名規則
所謂標識符,是指我們為變數(variable)、宏(macro),或者函式(function)等等取的名字。(在C語言中,標識符是對變數、函式標號和其它各種用戶定義對象的命名。)例如 int num; 這個語句中的 num 就是一個標識符。
1.長度限制
C89 規定,編譯器至少應該能夠處理 31 個字元(包括 31)以內的內部標識符(internalidentifier);而對於外部標識符(external identifier),編譯器至少應該能夠處理 6 個字元(包括 6)以內的外部標識符。
最新的 C99 標準規定,編譯器至少應該能夠處理 63 個字元(包括 63)以內的內部標識符;編譯器至少應該能夠處理 31 個字元(包括 31)以內的外部標識符。
事實上,我們可以使用超出最大數目限制的字元來命名標識符,不過編譯器會忽略超出的那部分字元。也就是說,如果我們用 35 個字元來命名變數,而那個編譯器最多只能處理 31 個字元的變數名的話,那么多出的那 4 個字元就會被編譯器忽略,只有前面的 31 個字元有效。有些古老的編譯器只能處理 8 個字元以內的標識符,對於這樣的編譯器來說,標識符 kamehameha 和 kamehameko 是等價的,因為它們前面 8 個字元相等。
2. 可用字元和組合規則
標準規定,標識符只能由大小寫英文字母,下劃線(_),以及阿拉伯數字組成。標識符的第一個字元必須是大小寫英文字母或者下劃線,而不能是數字。
作用域的類別
塊作用域
塊作用域(block scope),也即局部作用域(local scope)。其聲明區域典型為一對花括弧{ }括起來的程式塊。其內部聲明的名字的作用域從首次聲明之處至該塊的結束之處。如:局部變數、局部類型等的名字。
for、while、if、switch等語句也構成了塊作用域。
函式作用域
函式作用域(function scope),只適用標籤(label)的名字。標籤名字可以在它所出現的函式中的任何位置被goto語句使用。也就是說,在函式內部,可以先通過goto語句使用一個標籤名字,之後在該函式內部才有該標籤名字的定義。
函式原型作用域
函式原型作用域(function prototype scope)是指,函式原型中聲明的名字只在該原型結束前可見,在原型結束處即為作用域結束之處。例如:
voidfoo(int a, float b, vector<int> c);
全局作用域
全局命名空間作用域(global namespace scope),也稱作全局作用域(global scope)是指編譯單元的最外部的聲明區域中聲明的名字的作用域。這些名字在程式的各個編譯單元之間均可見。全局名字是連結器需要識別的名字。一般說來,全局名字對應的實體在程式中應僅有一份,而不同編譯單元都可以通過同一名字綁定到該實體上。
典型的具有全局作用域的名字,包括:全局變數、全局函式等。
C++規定,可以通過::來限定一個名字為全局作用域中的名字。這也是“全局命名空間”稱謂的來源。例如:
int i; //全局變數
namespace ns{int i;}
using namespace ns;
::i=102;//給全局變數i賦值,而不是ns::i賦值。這行如果不用“::”,則編譯錯誤: reference to 'i' is ambiguous|
檔案作用域
檔案作用域(file scope)是從名字聲明之處直至該編譯單元結束之處。靜態全局變數、內聯全局函式、const限定的全局變數等的名字的作用域均為典型的檔案作用域。
各種類型,不論是通過typedef、class、struct、union、enum等方式聲明或定義,如果不是內部類型,則名字都具有檔案作用域。也就是說,同一個類型的聲明與定義可以出現在不同的編譯單元,在連結時不產生名字衝突。實際上,編譯時並不把類型的名字放到連結器的輸入檔案中,連結器根本看不到類型的名字,所以也無可能產生名字衝突。甚至在不同的編譯單元分別適用了同一類型名字的完全不同的定義,也不會有編譯連結錯誤。例如:
//main.cpp
struct T{int i};
intmain(){
Tv;
v.i=101;
return 0;
}
//foo.cpp
struct T{float i};
voidfoo(){
Tv;
v.i=3.14;
}
C++引入了無名命名空間(unnamednamespace),其作用域即為當前編譯單元。例如:
namespace {int i;}
也可以通過前面加上::限定訪問檔案作用域中的名字。
命名空間作用域
主條目:命名空間
命名空間的成員名字具有命名空間作用域(namespace scope)。該名字的作用域從在該命名空間內的聲明點直至當前編譯單元內該命名空間的結尾處。使用using namespace語句可以提升該命名空間此時已經聲明的名字到該using語句的作用域。
使用::限定符,可以在命名空間名字的作用域內訪問該命名空間的成員名字。
類作用域
類(class、struct、union)內定義的名字的作用域稱為類作用域(class scope)。這些名字在當前類的定義內部,以及類定義詞法範圍外的類成員定義中是可見的。因此,在類內部,成員名字可以先使用後定義,不必前向聲明(forward declaration)。
類靜態數據成員具有外部連結屬性。
類的成員名字在其所在的類作用域內、或者派生類作用域內可見,或者通過 .運算符、->運算符、::限定符訪問。
匿名類的作用域
這裡的匿名類是指匿名struct、匿名class、匿名union,且沒有直接用這種類型定義了變數。如果緊隨這些無名類型的定義之後,定義了該類型的變數,則類型的定義及使用與普通情況完全一樣;嚴格說,這種情形可以不算是匿名類。
匿名類的作用域有特別含義:
匿名類作為嵌套類,即匿名類在一個外部類的內部定義:則編譯器就在此處定義一個該匿名類的無名變數,並把該匿名類的成員的名字提升到該類型定義所在的外部類的作用域內。由於匿名類不能使用點運算符訪問其成員,所以匿名類只能有數據成員,不允許有成員函式,也不能包含私有或受保護的數據成員。如果匿名類的定義是連續嵌套,則最內部的匿名類的成員名字被提升至最外部的非匿名類或可用變數訪問的成員類之處。
匿名類不作為嵌套類定義,即匿名類定義在一個全局函式內部或者全局函式外部。C/C++語言標準只允許匿名union在這種情形下定義;如果是匿名struct、匿名class,則編譯報錯。對於此種情形的匿名union,編譯器同樣在此處定義一個該匿名聯合的無名變數,並把該匿名聯合的成員的名字提升到該匿名聯合所在的作用域內,匿名聯合只能有數據成員,不允許有成員函式,也不能包含私有或受保護的數據成員。在函式外的匿名聯合只能在當前編譯單元內可見,因此必須使用static關鍵字,或者必須放在匿名命名空間中。
例如:
int main(){
union{
int test;
char c;
};
test=5; //匿名union的成員的名字提升到定義了該匿名union的作用域內。
struct { int i;} v; //匿名struct,但是緊隨其後聲明了一個變數v
v.i=101; // 編譯通過
i=102; //編譯報錯: 'i' wasnot declared in this scope|
return 0;
}
枚舉作用域
枚舉作用域(enumeration scope)是指枚舉類型的成員(enumerator)的名字的作用域,起自其聲明之處,終至枚舉定義結束之處。
C語言規定,枚舉類型的成員(enumerator)的可見範圍被提升至該枚舉類型所在的作用域內。這被認為有可能污染了外部的作用域。為此,C++11引入了枚舉類(enum class)解決此問題。