Go 学习笔记(6)— 变量定义、变量声明、变量作用域
1. 變量定義
Go 語言變量名由字母、數字、下劃線組成,其中首個字符不能為數字。聲明變量的一般形式是使用 var 關鍵字:
var varName dataType [= value]
Go 語言和許多編程語言不同,它在聲明變量時將變量的類型放在變量的名稱之后。
這樣做的好處就是可以避免像 C 語言中那樣含糊不清的聲明形式,例如:
// C 語言寫法
int* a, b, c;
// 創建一個 int 型指針 a 和兩個 int 型變量 b,c
而在 Go 中,則可以輕松地將它們都聲明為指針類型。
2. 變量聲明
當一個變量被聲明之后,系統自動賦予它該類型的零值: int 為 0, float 為 0.0, bool 為 false, string 為空字符串,指針為 nil 等。所有的內存在 Go 中都是經過初始化的。
在C語言中,變量在聲明時,并不會對變量對應內存區域進行清理操作。此時,變量值可能是完全不可預期的結果。開發者需要習慣在使用C語言進行聲明時要初始化操作,稍有不慎,就會造成不可預知的后果。
在網絡上只有程序員才能看懂的“燙燙燙”和“屯屯屯”的梗,就來源于 C/C++ 中變量默認不初始化。
微軟的 VC 編譯器會將未初始化的棧空間以 16 進制的 0xCC 填充,而未初始化的堆空間使用 0xCD 填充,而 0xCCCC 和 0xCDCD 在中文的 GB2312 編碼中剛好對應“燙”和“屯”字。
因此,如果一個字符串沒有結束符
\0,直接輸出的內存數據轉換為字符串就剛好對應“燙燙燙”和“屯屯屯”。
變量的命名規則遵循駱駝命名法,即首個單詞小寫,每個新單詞的首字母大寫,例如: numShips 和 startDate 。
2.1 單變量聲明
- 指定變量類型,聲明后若不賦值,則使用該類型的默認值;
var varName dataType = value
- 根據值由編譯器自行判定變量類型;
var varName = value
- 省略關鍵字
var, 注意:=左側的變量不應該是已經聲明過的,否則會導致編譯錯誤,該種聲明只能使用于函數或者方法;
varName := value
例如:
var a int = 10var b = 10c := 10
使用示例:
package mainvar a = "臥虎藏龍"
var b string = "www.wohu.com"
var c boolfunc main() {d := 100println(a, b, c, d)
}
2.2 多變量聲明
- 指定變量類型,聲明后若不賦值,則使用默認值;
var name1 name2 name3 int = v1, v2, v3
- 根據值自行判定變量類型;
var name1, name2, name3 = v1, v2, v3
- 省略 var, 注意 := 左側的變量不應該是已經聲明過的,否則會導致編譯錯誤;
name1, name2, name3 := v1, v2, v3
- 全局變量
這種因式分解關鍵字的寫法一般用于聲明全局變量
var (name1 typename2 type)
注意: 一般建議一行只寫一個變量,這樣方便代碼閱讀。
在 Go 語言中,定義變量要么通過聲明、要么通過 make 和 new 函數,不一樣的是 make 和 new 函數屬于顯式聲明并初始化。
如果我們聲明的變量沒有顯式聲明初始化,那么該變量的默認值就是對應類型的零值。從下面的表格可以看到,可以稱為引用類型的零值都是 nil。
| 類型 | 零值 |
|---|---|
| int、float | 0 |
| bool | false |
| string | “” 空字符串 |
| struct | 內部字段的零值 |
| slice | nil |
| map | nil |
| 指針 | nil |
| 函數 | nil |
| chan | nil |
| interface | nil |
3. 使用示例
package mainvar x, y int
var (a intb bool
)
var c, d int = 1, 2// "hello" 是字符串必須用雙引號 " " 包含,如果用單引號包含則會報錯,單引號只能包含單個字符
// invalid character literal (more than one character)var e, f = 123, "hello"// 這種不帶聲明格式的只能在函數體中出現,而不可以用于全局變量的聲明與賦值
// g, h := 123, "hello"func main() {g, h := 123, "hello"println(x, y, a, b, c, d, e, f, g, h)
}
運行結果:
0 0 0 false 1 2 123 hello 123 hello
4. 注意事項
在相同的代碼塊中,我們不可以再次對于相同名稱的變量使用初始化聲明,如下代碼
package mainimport "fmt"func main() {var a int = 2fmt.Println("a is ", a)a := 5fmt.Println("a is ", a)
}
編譯器會報錯誤
# command-line-arguments
src/main.go:32:4: no new variables on left side of :=
但是 a = 20 是可以的,因為這是給相同的變量賦予一個新的值。
-
如果你在定義變量
a之前使用它,則會得到編譯錯誤undefined: a。 -
如果聲明了一個局部變量卻沒有在相同的代碼塊中使用它,同樣會得到編譯錯誤
a declared and not used -
空白標識符也被用于拋棄值,如值 5 在
_, b = 5, 7中被拋棄。
_ 也叫作匿名變量,匿名變量不占用內存空間,不會分配內存。匿名變量與匿名變量之間也不會因為多次聲明而無法使用。 實際上是一個只寫變量,你不能得到它的值。這樣做是因為 Go 語言中你必須使用所有被聲明的變量,但有時你并不需要使用從一個函數得到的所有返回值。
注意:在多個短變量聲明和賦值中,至少有一個新聲明的變量出現在左值中,即便其他變量名可能是重復聲明的,編譯器也不會報錯,代碼如下,編譯器不會報 err 重復定義。
conn, err := net.Dial("tcp", "127.0.0.1:8080")
conn2, err := net.Dial("tcp", "127.0.0.1:8080")
5. 變量作用域
Go 語言中變量可以在三個地方聲明:
-
函數內定義的變量稱為局部變量
-
函數外定義的變量稱為全局變量
-
函數定義中的變量稱為形式參數
- 全局作用域
在任何地方都可以訪問的變量,稱其具有全局作用域。在 Go 語言中,全局作用域有兩類:
-
Go語言內置的預聲明標識符(包括預聲明的類型名、關鍵字、內置函數等)它們具有全局作用域,在任意命名空間內都可見。 -
Go語言包內以大寫字母開頭的標識符( 包括變量、常量、函數和方法名、自定義類型、結構字段等),它們具有全局作用域,在任意命名空間內都可見。
- 包內作用域
在 Go 語言包內定義的以小寫字母開頭的標識符(變量、常量、函數和方法名、自定義類型、結構字段等〉,它們在本包可見,在其他包都是不可見的,這些標識符具有包內作用域。
- 隱式作用域
每個代碼塊內定義的變量稱為“局部變量”這些局部變量只在當前代碼塊內可見,其作用域屬于當前代碼塊的隱式作用域。
不要將作用域和生命周期混為一談。聲明語句的作用域對應的是一個源代碼的文本區域;它是一個編譯時的屬性。一個變量的生命周期是指程序運行時變量存在的有效時間段,在此時間區域內它可以被程序的其他部分引用;是一個運行時的概念。
句法塊是由花括弧所包含的一系列語句,就像函數體或循環體花括弧包裹的內容一樣。句法塊內部聲明的名字是無法被外部塊訪問的。這個塊決定了內部聲明的名字的作用域范圍。
我們可以把塊(block)的概念推廣到包括其他聲明的群組,這些聲明在代碼中并未顯式地使用花括號包裹起來,我們稱之為詞法塊。對全局的源代碼來說,存在一個整體的詞法塊,稱為全局詞法塊;對于每個包、每個 for 、 if 和 switch 語句,也都有對應詞法塊;
每個 switch 或 select 的分支也有獨立的詞法塊;當然也包括顯式書寫的詞法塊(花括弧包含的語句)。
和 for 循環類似, if 和 switch 語句也會在條件部分創建隱式詞法域,還有它們對應的執行體詞法域。下面的 if-else 測試鏈演示了 x 和 y 的有效作用域范圍:
if x := f(); x == 0 {fmt.Println(x)
} else if y := g(x); x == y {fmt.Println(x, y)
} else {fmt.Println(x, y)
}
fmt.Println(x, y) // compile error: x and y are not visible here
第二個 if 語句嵌套在第一個內部,因此第一個 if 語句條件初始化詞法域聲明的變量在第二個 if 中也可以訪問。 switch 語句的每個分支也有類似的詞法域規則:條件部分為一個隱式詞法域,然后是每個分支的詞法域。
if f, err := os.Open(fname); err != nil { // compile error: unused: freturn err
}
f.ReadByte() // compile error: undefined f
f.Close() // compile error: undefined f
變量 f 的作用域只在 if 語句內,因此后面的語句將無法引入它,這將導致編譯錯誤。你可能會收到一個局部變量 f 沒有聲明的錯誤提示,具體錯誤信息依賴編譯器的實現。
通常需要在 if 之前聲明變量,這樣可以確保后面的語句依然可以訪問變量:
f, err := os.Open(fname)
if err != nil {return err
}
f.ReadByte()
f.Close()
5.1 局部變量
在函數體內聲明的變量稱之為局部變量,它們的作用域只在函數體內,參數和返回值變量也是局部變量。
package mainimport "fmt"func main() {/* 聲明局部變量 */var a, b, c int /* 初始化參數 */a = 10b = 20c = a + bfmt.Printf ("結果: a = %d, b = %d and c = %d\n", a, b, c)
}
5.2 全局變量
在函數體外聲明的變量稱之為全局變量,全局變量聲明必須以 var 關鍵字開頭,如果想要在外部包中使用全局變量的首字母必須大寫。全局變量可以在整個包甚至外部包(被導出后)使用。
package mainimport "fmt"/* 聲明全局變量 */
var g intfunc main() {/* 聲明局部變量 */var a, b int/* 初始化參數 */a = 10b = 20g = a + bfmt.Printf("結果: a = %d, b = %d and g = %d\n", a, b, g)
}
Go 語言程序中全局變量與局部變量名稱可以相同,但是函數內的局部變量會被優先考慮。
package mainimport "fmt"/* 聲明全局變量 */
var g int = 20func main() {/* 聲明局部變量 */var g int = 10fmt.Printf ("結果: g = %d\n", g)
}
5.3 形式參數
package mainimport "fmt"/* 聲明全局變量 */
var a int = 20;func main() {/* main 函數中聲明局部變量 */var a int = 10var b int = 20var c int = 0fmt.Printf("main()函數中 a = %d\n", a);c = sum( a, b);fmt.Printf("main()函數中 c = %d\n", c);
}/* 函數定義-兩數相加 */
func sum(a, b int) int {fmt.Printf("sum() 函數中 a = %d\n", a);fmt.Printf("sum() 函數中 b = %d\n", b);return a + b;
}
以上實例執行輸出結果為:
main()函數中 a = 10
sum() 函數中 a = 10
sum() 函數中 b = 20
main()函數中 c = 30
總結
以上是生活随笔為你收集整理的Go 学习笔记(6)— 变量定义、变量声明、变量作用域的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Go 学习笔记(4)— Go 标识符、数
- 下一篇: 女人与鸟的画是谁画的啊?