iT邦幫忙

2021 iThome 鐵人賽

DAY 4
0
Modern Web

fmt.Println("從零開始的Golang生活")系列 第 4

Day4 Variable

Background

如同前一章節的import一樣,在Go的世界當中是不允許浪費任何資源的,因此只要變數進行了宣告就需要被使用,否則會出現variable declared and not used的錯誤。

但有一例外,那就是變數名稱命名為 _ , 這表示你並不為該變數進行宣告。

程式碼解說

變數宣告

首先我們一樣創建一個名為variable.go的文件,並在該文件進行解說與範例撰寫。

var Name Type = Expression

package main

import "fmt"

func main()  {
  var x int = 8
	var a = 16
	var b int
	var _ = 10
	fmt.Println(x, a, b)
}

運行後可得結果

8 16 0
  1. 如果省略型別Type,會由表達式Expression決定Type
  2. 如果省略表達式Expression,會由型別Type來決定初始值為多少,常見的初始值有int之於0, bool之於false, string之於" "...等
  3. 若需要宣告一個全域變數只能透過var

短宣告

Name := Expression

宣告declare(:=) 跟 指派assign(=)是不同的,一次宣告多個變數,若已宣告過的變數會自動變成assign效果

func main()  {
	var b int = 123
	a, b := 100, 99
	c, b := 0, 1
	fmt.Println(a, b, c)
}

運行後可得結果

100 1 0

短宣告方便,常常使用來宣告初始化大量、生命週期短的區域變數(像是迴圈內的i, j變數)。

反之一般宣告則用來宣告長週期且型別明確的變數。

此外,如果看到下面類型的變數短宣告

result, err := ...

result, err都將會是區域變數喔。

常數宣告

無法更動、無法刪除的變數,但用法跟變數var一樣,const後面可以接任何型別,

因為常數是無法更動的,因此常數名稱通常為全大寫

const PI = 3.14159
const HELLO = "Hello World"

ITOA

iota希臘字符,在Golang中是關鍵字之一,用在宣告常數中,

效果為數字遞增iota本身數值從0開始,

主要讓工程師不用手動打遞增數像是0、1、2、3...。

const (
	A = iota       // 0
	B              // 1
	_              // 2 佔位符也會被計算
	C              // 3
	D = iota * 0.1 // 0.4 接續前面的 iota
	E              // 0.5
	F              // 0.6
	G              // 0.7
)

起始值也不一定要從0開始

const (
	X = iota + 100 // 100
	Y              // 101
	Z              // 102
)

也常被拿來作 左移右移(Shift Bit) 運算

const (
	b1 = 1 << iota // 1  右側被塞入0個bit (2^0 二的零次方)
	b2             // 2  右側被塞入1個bit (2^1 二的一次方)
	b3             // 4  右側被塞入2個bit
	b4             // 8  
	b5             // 16 
)

變數注意事項

變數是否被overwrite(1)

package main

import "fmt"

var a = "Hello!"

func main()  {
	b := 10
	fmt.Println(&a, a, &b, b)

	a, b := 100, 99
	fmt.Println(&a, a, &b, b)
}

運算後可得結果

0x1157c80 Hello! 0xc000118000 10
0xc000118008 100 0xc000118000 99

只要有對相同變數名稱再次進行宣告,代表宣告新的變數名稱以及使用新的記憶體空間,

這也意味著原本的全域變數a的地址被覆蓋掉了。

變數是否被overwrite(2)

package main

import "fmt"

var i = 100

func main() {
	fmt.Println(&i)

	i = 200
	fmt.Println(&i)

	i := 150
	fmt.Println(&i)

	i, j := 250, 100
	fmt.Println(&i, j)
}
fmt.Println(i, &i)

運算後可得結果

0x1149268
0x1149268
0xc0000ae008
0xc0000ae008 100

並非進行宣告或是短宣告後就會有全新的變數出現並挪用全新的記憶體空間,

Go會去尋找目前最小單位的區塊內是否已經有被宣告的變數,沒有的話才會宣告新的。

  1. 一開始宣告一個全域變數i為100,並且i的地址為0x1149268
  2. 然後賦予全域變數i值為200,這時候還是對同一個變數進行操作,因此地址依然為0x1149268
  3. 接下來宣告一個區域變數i為150,由於該區域之前並未有其他i變數的存在,因此產生一個新的區域變數i,並賦予新的地址0xc0000ae008,同時也意味著全域變數i的地址被覆蓋了
  4. 宣告兩個區域變數i與j,但該區域先前已經有了i這個變數的存在,因此直接將i的值變為250即可,所以i的地址依然還是先前的0xc0000ae008

變數全新與否

看到有var會是全新的。

但看到:=卻是不一定,短變數宣告可以伴隨著未宣告的變數一起出現。

package main

import "fmt"

var x = 123

func main() {
	fmt.Println(&x)

	var x = 123
	fmt.Println(&x)

	x, y := 123, 100
	fmt.Println(&x, &y)

	if true {
		var x, y = 123, 100
		// 可以換成短變數宣告。

		fmt.Println(&x, &y)
	}

	fmt.Println(&x)
}

運行後可得結果

0x1149268
0xc00019a010
0xc00019a010 0xc00019a018
0xc00019a020 0xc00019a028
0xc00019a010
  1. 宣告一全域變數x值為123,並得到x地址為0x1149268
  2. 宣告一區域(main scope)變數x值為123,並得到x地址為0xc00019a010
  3. 短宣告二區域(main scope)變數x, y 值為123, 100,但由於x變數在此區間先前以宣告過故依然使用地址0xc00019a010,y地址則為0xc00019a018
  4. 宣告兩區域(true scope)變數x, y值為123, 100,且由於x, y在該區域並未宣告過,因此此時x, y地址分別為 0xc00019a020, 0xc00019a028
  5. 出了true scope並回到main scope後,所得的x地址就會回到main scope的x地址,地址為0xc00019a010

Summary

這章節只替大家帶來基本變數的宣告與使用,並透過記憶體位置的範例讓大家知道區域與全域變數在Go中的規則與變化,希望大家能避開這些坑。

下個章節則會透過不同型態的變數為各位解說。


上一篇
Day3 First Go application
下一篇
Day5 Type
系列文
fmt.Println("從零開始的Golang生活")30

尚未有邦友留言

立即登入留言