golang 快速入門 [8.1]-變數型別、宣告賦值、作用域宣告週期與變數記憶體分配
golang 快速入門 [8.1]-變數型別、宣告賦值、作用域宣告週期與變數記憶體分配
前文
- golang 快速入門 [1]-go 語言導論
- golang 快速入門 [2.1]-go 語言開發環境配置-windows
- golang 快速入門 [2.2]-go 語言開發環境配置-macOS
- golang 快速入門 [2.3]-go 語言開發環境配置-linux
- golang 快速入門 [3]-go 語言 helloworld
- golang 快速入門 [4]-go 語言如何編譯為機器碼
- golang 快速入門 [5.1]-go 語言是如何執行的-連結器
- golang 快速入門 [5.2]-go 語言是如何執行的-記憶體概述
- golang 快速入門 [5.3]-go 語言是如何執行的-記憶體分配
- golang 快速入門 [6.1]-整合開發環境-goland 詳解
- golang 快速入門 [6.2]-整合開發環境-emacs 詳解
- golang 快速入門 [7.1]-專案與依賴管理-gopath
- golang 快速入門 [7.2]-北冥神功—go module 絕技
題記
- 在上一篇文章中,我們介紹了吸心大法—go module 的技巧來引用第三方程式碼,但是看過武俠小說的同學都知道,只有內力沒有招式那也是花架子。正所謂"巧婦難為無米之炊",我們將在後面幾章鞏固基本功,介紹 go 語言的語法、基本概念和性質。 ## 前言 我們將在本文中學習到:
- 變數的內涵
- 變數的資料型別
- 變數的多種宣告與賦值
- 變數的命名
- 變數的作用域與示例
- 變數的記憶體分配方式
變數是什麼
- 在計算機程式設計中,變數 (Variable) 是與關聯的
符號名
配對的儲存地址 (記憶體地址標識) - 變數用於儲存要在計算機程式中引用和操作的資訊。變數還提供了一種使用描述性名稱標記資料的方法,因此讀者和開發人員都可以更清楚地理解程式。可以將將變數視為儲存資訊的空間。
- 編譯器必須用資料的實際地址替換變數的符號名。儘管變數的名稱,型別和地址通常保持不變,但儲存在地址中的資料可能會在程式執行期間發生更改。
變數的資料型別
go 語言是靜態型別的語言,需要在執行前明確變數的資料型別以及大小。如下圖是靜態語言與動態語言的區別。動態語言可以在執行時擴充套件變數的大小。
資料型別是資料的屬性,它告訴編譯器打算如何使用資料
大多數程式語言都支援基本資料型別,包括整數,浮點數,字元和布林值等。資料型別定義了可以對資料執行的操作,資料的含義以及該型別值的儲存方式
Go 語言的數值型別包括幾種不同大小的整數、浮點數和複數。每種數值型別都決定了對應的大小範圍和是否支援正負符號。讓我們先從整型數型別開始介紹
Go 語言同時提供了有符號和無符號型別的整數運算,有 int8、int16、int32 和 int64 四種截然不同大小的有符號整型數型別,分別對應 8、16、32、64bit 大小的有符號整型數,與此對應的是 uint8、uint16、uint32 和 uint64 四種無符號整型數型別。
還有兩種一般對應特定 CPU 平臺機器字大小的有符號和無符號整數 int 和 uint。其中 int 是應用最廣泛的數值型別。這兩種型別都有同樣的大小,32 或 64bit,但是我們不能對此做任何的假設;因為不同的編譯器卽使在相同的硬體平臺上可能產生不同的大小。
Unicode 字元 rune 型別是和 int32 等價的型別,通常用於表示一個 Unicode 碼點。這兩個名稱可以互換使用。同樣 byte 也是 uint8 型別的等價型別,byte 型別一般用於強調數值是一個原始的資料而不是一個小的整數。
最後,還有一種無符號的整數型別 uintptr,沒有指定具體的 bit 大小但是足以容納指標。uintptr 型別只有在底層程式設計是才需要,特別是 Go 語言和 C 語言函式庫或作業系統介面相互動的地方。在介紹指標時,會詳細介紹它。
變數的宣告與賦值
變數的宣告使用var
來標識,變數宣告的通用格式如下:
var name type = expression
- 函式體外部:變數宣告方式 1
var i int
- 函式體外部:變數宣告方式 2
// 外部連續宣告
var U, V, W float64
- 函式體外部:變數宣告方式 3
// 賦值不帶型別,自動推斷
var k = 0
- 函式體外部:變數宣告方式 4
// 外部連續宣告+賦值
var x, y float32 = -1, -2
-
函式體外部:變數宣告方式 5
// 外部var括號內部 var ( g int u, v, s = 2.0, 3.0, "bar" )
-
函式體內部:變數宣告方式 6
func main() { //函式內部的變數宣告 宣告的變數型別必須使用 否則報錯 var x string
-
函式體內部:變數宣告方式 7
// 只限函式內部 自動推斷型別 y := "jonson"
變數的命名
- 名字的長度沒有邏輯限制,但是 Go 語言的風格是儘量使用短小的名字,對於區域性變數尤其是這樣;你會經常看到 i 之類的短名字,而不是冗長的 theLoopIndex 命名。通常來說,如果一個名字的作用域比較大,生命週期也比較長,那麼用長的名字將會更有意義。
- 在習慣上,Go 語言程式設計師推薦使用 駝峰式 命名,當名字由幾個單片語成時優先使用大小寫分隔,而不是優先用下劃線分隔。因此,在標準庫有 QuoteRuneToASCII 和 parseRequestLine 這樣的函式命名,但是一般不會用 quote_rune_to_ASCII 和 parse_request_line 這樣的命名。而像 ASCII 和 HTML 這樣的縮略詞則避免使用大小寫混合的寫法,它們可能被稱為 htmlEscape、HTMLEscape 或 escapeHTML,但不會是 escapeHtml。
作用域
- 在程式設計中,一段程式程式碼中所用到的識別符號並不總是有效/可用的,作用域就是識別符號有效可用的程式碼範圍。
- 在 go 語言中,作用域可以分為
全域性作用域 > 包級別作用域 > 檔案級別作用域 > 函式作用域 > 內部作用域 universe block > package block > file block > function block > inner block
全域性作用域
- 全域性作用域主要是 go 語言預宣告的識別符號,所有 go 檔案都可以使用。主要包含了如下的識別符號
內建型別: int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
內建常量: true false iota nil
內建函式: make len cap new append copy close delete
complex real imag
panic recover
包級別作用域
- 全域性(任何函式之外)宣告的常量,型別,變數或函式的識別符號是包級別作用域
- 如下例中的變數 x 以及 fmt 包中的函式
println
就是包級別作用域
package main
import "fmt"
var x int=5
func main(){
fmt.Println("mainx:",x)
}
- 呼叫例子 1
// f1.go
package main
var x int
//-------------------------------------
// f2.go
package main
func f() {
fmt.Println(x)
}
- 呼叫例子 2:呼叫另一個包中的函式和屬性:
//testdemo/destdemo.go
package testdemo
import "fmt"
var Birth uint = 23
func Haha(){
fmt.Println("lalalal")
}
//-------------------------------------
package main // main/scope.go
import (
"testdemo"
"fmt"
)
func main(){
testdemo.Haha()
fmt.Println(testdemo.Birth)
}
- 注意:如果要讓包中的屬性和變數被外部包呼叫,必須要首字母大寫。
檔案級別作用域
- import 包的識別符號是檔案級別作用域的,只能夠在本檔案中使用
- 例如下面的程式碼無效,因為 import 是 file block,不能跨檔案
// f1.go
package main
import "fmt"
//-------------------------------------
// f2.go 無效
package main
func f() {
fmt.Println("Hello World")
}
函式級別作用域
- 方法接收者(後面介紹),函式引數和結果變數的識別符號的範圍是函式級別作用域,在函式體外部無效,在內部任何位置可見
- 例如下面函式中的 a,b,c 就是函式級別作用域
func add(a int,b int)(c int) {
fmt.Println("Hello World")
x := 5
fmt.Println(x)
}
內部作用域
- 函式宣告的常量和變數是函式內部作用域,其作用域從宣告開始,到最近的一個花括號結束。
- 例子 1:注意引數的前後順序
//下面的程式碼無效:
func main() {
fmt.Println("Hello World")
fmt.Println(x)
x := 5
}
- 例子 2:引數不能跨函式使用
//下面的程式碼無效2:
func main() {
fmt.Println("Hello World")
x := 5
fmt.Println(x)
}
//
func test(){
fmt.Println(x)
}
- 例子 3:函式內部變數與外部變數重名,使用就近原則
package main
import "fmt"
var x int=5
func test(){
var x int = 99;
x = 100;
// 下面的程式碼輸出結果為: 100
fmt.Println("testx",x)
}
- 例子 4:內部花括號
- 變數 x 的作用域是從 scope3 到 scope5 為止
func main() {
fmt.Println("Hello World") // scope1
{ // scope2
x := 5 // scope3
fmt.Println(x) // scope4
} // scope5
}
變數的記憶體分配
我們在前文go 語言是如何執行的-記憶體概述 與 go 語言是如何執行的-記憶體分配 中,詳細介紹了在虛擬記憶體角度其不同的段
及其功能
- 對於全域性變數,其儲存在
.data
和.bss
段。 我們可以用下面的例項來驗證
// main.go
package main
var aaa int64 = 8
var ddd int64
func main() {
}
- 在終端中輸入如下指令列印彙編程式碼
$ go tool compile -S main.go
...
"".aaa SNOPTRDATA size=8
0x0000 08 00 00 00 00 00 00 00
"".ddd SNOPTRBSS size=8
...
- 從上面的彙編輸出中可以看出, 變數
aaa
位於.data
段中, 變數ddd
位於.bss
段 - 對於函式的內部變數,在 go 語言的程式設計規範中並沒有明確的劃分,變數是分配在棧中還是堆中,簡單來說,Go 語言的逃逸分析 (escape analysis) 會分析各個變數的使用狀況,來決定他要放在 stack 還是 heap 段。
- 一般的變數會在執行時在棧中建立,隨著函式的呼叫而產生,隨著函式的結束而消亡。如果編譯器無法證明函式返回後未引用該變數,則編譯器必須在堆上分配該變數,以避免懸空指標錯誤。另外,如果區域性變數很大,則將其儲存在堆而不是堆疊上可能更有意義
- 有一些初始化的情況會被分配到
.data
段中,例如長度大於4
的陣列字面量、字串
等,如下所示func main() { var vvv = [5]int{1,2,3,4,5} var bbb string = "hello" }
- 在終端中輸入如下指令列印彙編程式碼 即可驗證其存在於
.data
段中
$ go tool compile -S main.go
...
go.string."hello" SRODATA dupok size=5
0x0000 68 65 6c 6c 6f hello
type.[5]int SRODATA dupok size=72
"".ddd SNOPTRBSS size=8
...
總結
參考資料
喜歡本文的朋友歡迎點贊分享~
唯識相鏈啟用微信交流群(Go 與區塊鏈技術)
歡迎加微信:ywj2271840211
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- go語言變數的宣告與賦值Go變數賦值
- 變數、作用域與記憶體變數記憶體
- Kotlin 變數詳解:宣告、賦值與最佳實踐指南Kotlin變數賦值
- JavaScript 同時宣告多個變數並賦值JavaScript變數賦值
- TypeScript基礎入門-變數宣告(一)TypeScript變數
- var與let宣告變數的區別變數
- JavaScript 宣告變數JavaScript變數
- TypeScript 變數宣告TypeScript變數
- javascript:變數、作用域和記憶體問題JavaScript變數記憶體
- SCSS 變數的宣告CSS變數
- python變數與變數作用域Python變數
- JS變數宣告和函式宣告提升JS變數函式
- 《前端實戰總結》之變數提升,函式宣告提升及變數作用域詳解前端變數函式
- Go:變數宣告的多種方法與預設值Go變數
- JavaScript 變數與函式宣告前置JavaScript變數函式
- Python 3 快速入門 1 —— 資料型別與變數Python資料型別變數
- var、let、const宣告變數的區別變數
- ES6 變數宣告與賦值:值傳遞、淺拷貝與深拷貝詳解變數賦值
- 宣告變數的問題變數
- Mysql變數宣告的方式MySql變數
- js宣告全域性變數JS變數
- 變數宣告帶var與不帶var的區別變數
- JavaScript變數宣告帶var與不帶var區別JavaScript變數
- go語言 變數的宣告與使用Go變數
- 第4章 變數、作用域和記憶體問題變數記憶體
- golang變數作用域問題-避免使用全域性變數Golang變數
- 淺談JS變數宣告和函式宣告提升JS變數函式
- 深入解析JS變數宣告和函式宣告提升JS變數函式
- Python入門(十八):變數作用域與巢狀函式Python變數巢狀函式
- C++與Rust變數宣告的比較C++Rust變數
- 變數作用域變數
- 『忘了再學』Shell基礎 — 19、使用declare命令宣告變數型別變數型別
- ECMAScript 6入門 - 變數的解構賦值變數賦值
- shell變數命名與賦值變數賦值
- Golang併發之共享記憶體變數Golang記憶體變數
- JavaScript - 變數、值、型別JavaScript變數型別
- 變數物件與作用域鏈變數物件
- C++ 煉氣期之變數的生命週期和作用域C++變數