go程式結構

卖油条的大叔發表於2024-06-02

1、命名規則

  • 名稱的開頭是字母或者下劃線,後面可以跟任意數量的字元、數字或者下劃線。

  • 區分大小寫,hello和Hello是不同的名稱

  • 實體第一個字母的大小寫決定其可見性是否跨包。

  • 包名本身總是由小寫字母組成

  • 名稱本身沒有長度限制,傾向使用短名稱,並且駝峰式風格。

2、 變數(var)

宣告通用格式var name type = expression

  • 型別(type)和表示式(expression)可以省略一個,但是不可以都省略。

  • 型別(type)省略:型別由表示式決定

  • 表示式(expression)省略:其初始值對應著型別的零值。

// 可以宣告一個變數列表,忽略型別允許宣告多個不同型別的變數
var i, j, k int  // int, int, int
var b, f, s = true, 2.3, "four" // bool, float64, string

go的零值機制保障所有的變數都是良好定義的,不存在未初始化變數。

型別 零值
數字 0
布林值 false
字串 空字串("")
介面和引用型別(slice、指標、map、通道、函式) nil
複合型別(陣列或結構體) 所有元素或者成員的零值

短變數宣告通用格式name := expression

  • 區域性變數的宣告和初始化中主要使用短宣告

  • :=表示宣告,單純的=表示賦值。 i, j = j, i // 表示交換i和j的值

  • 短變數宣告最少宣告一個新變數,否則程式碼無法編譯透過。

  • 多變數宣告中的已宣告變數,短宣告的行為等同於賦值。

指標&x

  • 變數是儲存值的地方,指標的值是一個變數的地址。

  • 指標可進行比較。指標相等的情況:

    • 指向同一個變數

    • 都是nil

new函式

  • new(T)建立一個T型別變數,初始化T型別的零值,並返回其地址。

  • 每一次呼叫new返回具有唯一地址的不同變數

i := new(int)
j := new(int)
fmt.Println(i == j) // false

變數的生命週期

  • 包級別變數的生命週期是整個程式的執行時間

  • 區域性變數有一個動態的生命週期

  • 垃圾回收期會追溯區域性變數的源頭,判斷其路徑是否可達,來進行回收

  • 每一次變數逃逸都需要一次額外的記憶體分配過程。下面程式碼變數x使用堆空間。

var global *int
func f() {
	// 變數x從函式f中逃逸
	// 雖然x被宣告為區域性變數,但f函式返回後還可以從global訪問
	var x int
	x = 1
	global = &x
}

3、賦值

多重賦值

  • 用於交換值x, y = y, x

  • 函式返回,左邊變數數量需要與返回的數量保持一致

    函式返回ok的bool型別變數的幾種情況:

    v, ok = m[key]  //map查詢
    v, ok = x.(T)   //型別斷言
    v, ok = <-ch    //通道接收
    

4、型別宣告

type宣告定義一個新的命名型別。

每個型別T,可以透過T(x)將值x轉換為型別T。

可以轉換的條件是:底層型別相同或者指向相同底層型別變數的未命名指標型別。

命名型別的值可以與其相同型別的值或者底層型別相同的未命名型別的值比較。

// 即使A和B底層型別相同,它們依舊是不同型別
type A float64
type B float64
var a A
var b B
fmt.Println(a == 0) // true
fmt.Println(b >= 0) // true
fmt.Println(a == b) // 編譯錯誤,型別不匹配

5、包和檔案

  • 變數在包外面的可見性透過首字母是否大寫來判斷。

初始化

  • 包的初始化按照在程式中匯入的順序來進行,自下向上,依賴順序優先。

    包p匯入了包q,包q會先進行初始化

  • init函式不能被呼叫和被引用。在包初始化時自動執行。

作用域

  • 程式可以包含多個同名的宣告,前提是它們在不同的詞法塊中

  • 內層和外層都存在相同宣告,內層宣告將覆蓋外層宣告

  • 編譯器查詢名字引用時,將自內向外查詢。若未找到會報錯。

//for迴圈會建立隱式的詞法塊
x := "hello"
for _, x := range x {
	x := x + 'A' - 'a'
	fmt.Printf("%c", x) //輸出hello
}
if x := f(); x == 0 {
	fmt.Println(x)
} else if y := g(x); x == y {
	fmt.Println(x, y) //編譯成功,第二個if語句巢狀在第一個中,所以x在此處可見
} else {
	fmt.Println(x, y)
}
fmt.Println(x, y) //編譯錯誤,x與y不可見
var cwd string
func init() {
	cwd, err := os.Getwd() // 注意:錯誤。cwd和err在內部未宣告,所以會將他們宣告為區域性變數
	//var err error        // 該註釋內容為正確寫法
	//cwd, err = os.Getwd()
	if err != nil {
		fmt.Println(err)
		return
	}
}

相關文章