Go程式語言2 7 作用域

2021-09-23 16:23:25 字數 2448 閱讀 6386

2.7 作用域

宣告將名字和程式實體關聯起來,如乙個函式或乙個變數。宣告的作用域是指用到宣告時所宣告名字的源**段。

不要將作用域和生命週期混淆。宣告的作用域是宣告在程式文字**現的區域,它是乙個編譯時屬性。變數的生命週期是變數在程式執行期間能被程式的其他部分所引用的起止時間,它是乙個執行時屬性。

語法塊(block)是由大括號圍起來的乙個語句序列,比如乙個迴圈體或函式體。在語法塊內部宣告的變數對塊外部不可見。塊把宣告包圍起來,並且決定了它的可見性。我們可以把塊的概念推廣到其他沒有顯式包含在大括號中的宣告**,將其統稱為詞法塊。包含了全部源**的詞法塊,叫作全域性塊。每乙個包,每乙個檔案,每乙個for、if和switch語句,以及switch和select語句中的每乙個條件,都是寫在乙個詞法塊裡的。當然,顯式寫在大括號語法裡的**塊也算是乙個詞法塊。

乙個宣告的詞法塊決定宣告的作用域大小。像int、len和true等內建型別、函式或常量在全域性塊中宣告並且對於整個程式可見。在包級別(就是在任何函式外)的宣告,可以被同乙個包裡的任何檔案引用。匯入的包(比如在tempconv例子中的fmt)是檔案級別的,所以它們可以在同乙個檔案內引用,但是不能在沒有另乙個import語句的前提下被同乙個包中其他檔案中的東西引用。許多宣告(像tempconv.ctof函式中變數c的宣告)是區域性的,僅可在同乙個函式中或者僅僅是函式的一部分所引用。

控制流標籤(如break、continue和goto語句使用的標籤)的作用域是整個外層的函式(enclosing function)。

乙個程式可以包含多個同名的宣告,前提是它們在不同詞法塊中。例如可以宣告乙個和包級別變數同名的區域性變數。或者像2.3.3節展示的,可以宣告乙個叫作new的引數,即使它是乙個全域性塊中預宣告的函式。然而,不要濫用,重宣告所涉及的作用域越廣,越可能影響其他的**。

當編譯器遇到乙個名字的引用時,將從最內層的封閉詞法塊到全域性塊尋找其宣告。如果沒有找到,它會報「undeclared name」錯誤;如果在內層和外層塊都存在這個宣告,內層的將先被找到。這種情況下,內層宣告將覆蓋外部宣告,使它不可訪問:

在函式裡面,詞法塊可能巢狀很深,所以乙個區域性變數宣告可能覆蓋另乙個。很多詞法塊使用if語句和for迴圈這類控制流結構構建。下面的程式有三個稱為x的不同的變數宣告,因為每個宣告出現在不同的詞法塊。(這個例子只是用來說明作用域的規則,風格並不完美!)

表示式x[i]和x + 'a' - 'a'都引用了在外層宣告的x,稍後我們會解釋它。(注意,後面的表示式不同於unicode.toupper函式。)

如上所述,不是所有的詞法塊都對應於顯式大括號包圍的語句序列,有一些詞法塊是隱式的。for迴圈建立了兩個詞法塊:乙個是迴圈體本身的顯式塊,以及乙個隱式塊,它包含了乙個閉合結構,其中就有初始化語句中宣告的變數,如變數i。隱式塊中宣告的變數的作用域包括條件、後置語句(i++),以及for語句體本身。

下面的例子也有三個名字為x的變數,每乙個都在不同的詞法塊中宣告:乙個在函式體中,乙個在for語句塊中,乙個在迴圈體中。但只有兩個塊是顯式的:

像for迴圈一樣,除了本身的主體塊之外,if和switch語句還會建立隱式的詞法塊。下面的if-else鏈展示x和y的作用域:

第二個if語句巢狀在第乙個中,所以第乙個語句的初始化部分宣告的變數在第二個語句中是可見的。同樣的規則可以應用於switch語句:條件對應乙個塊,每個case語句體對應乙個塊。

在包級別,宣告的順序和它們的作用域沒有關係,所以乙個宣告可以引用它自己或者跟在它後面的其他宣告,使我們可以宣告遞迴或相互遞迴的型別和函式。如果常量或變數宣告引用它自己,則編譯器會報錯。

在以下程式中:

f變數的作用域是if語句,所以f不能被接下來的語句訪問,編譯器會報錯。根據編譯器的不同,也可能收到其他報錯:區域性變數f沒有使用。

所以通常需要在條件判斷之前宣告f,使其在if語句後面可以訪問:

你可能希望避免在外部塊中宣告f和err,方法是將stat和close的呼叫放到else塊中:

通常go中的做法是在if塊中處理錯誤然後返回,這樣成功執行的路徑不會被變得支離破碎。

短變數宣告依賴乙個明確的作用域。考慮下面的程式,它獲取當前的工作目錄然後把它儲存在乙個包級別的變數裡。這通過在main函式中呼叫os.getwd來完成,但是最好可以從主邏輯中分離,特別是在獲取目錄失敗是致命錯誤的情況下。函式log.fatalf輸出一條訊息,然後呼叫os.exit(1)退出。

因為cwd和err在init函式塊的內部都尚未宣告,所以:=語句將它們都宣告為區域性變數。內層cwd的宣告讓外部的宣告不可見,所以這個語句沒有按預期更新包級別的cwd變數。

當前go編譯器檢測到區域性的cwd變數沒有被使用,然後報錯,但是不必嚴格執行這種檢查。進一步做乙個小的修改,比如增加引用區域性cwd變數的日誌語句就可以讓檢查失效。

全域性的cwd變數依然未初始化,看起來乙個普通的日誌輸出讓bug變得不明顯。

處理這種潛在的問題有許多方法。最直接的方法是在另乙個var宣告中宣告err,避免使用:=。

現在我們已經看到包、檔案、宣告以及語句是如何來構成程式的。接下來的兩章將要討論資料的結構。

Go基礎程式設計 作用域

鏈客,有問必答!小弟初學go語言,感覺作用域的運用還是很廣泛的,所以就寫上一點,也許有的都是很常見的,不過也沒關係,方便以後查詢。作用域為已宣告識別符號所表示的常量 型別 變數 函式或包在源 中的作用範圍。全域性變數 在函式體外宣告的變數稱之為全域性變數,全域性變數可以在整個包甚至外部包 被匯出後 ...

Go基礎程式設計 作用域規則

作用域為已宣告識別符號所表示的常量 型別 變數 函式或包在源 中的作用範圍。在函式體內宣告的變數 引數和返回值變數就是區域性變數,它們的作用域只在函式體內 func test a,b int func main i 20 err,i不屬於此作用域 if a 3 a 3 a 4 a在能if內部使用 在...

Go語言10 變數作用域

作用域為已宣告識別符號所表示的常量 型別 變數 函式或包在源 中的作用範圍。go 語言中變數可以在三個地方宣告 接下來讓我們具體了解區域性變數 全域性變數和形式引數。在函式體內宣告的變數稱之為區域性變數,它們的作用域只在函式體內,引數和返回值變數也是區域性變數。以下例項中 main 函式使用了區域性...