環境
Windows10
Visual Studio Code (Go for Visual Studio Code,Code Runner)
golang 1.14
##1、変数
静的型付き言語として、go言語はいつも固定的なデータ型を持ち、データ型は変数のメモリ領域上の大きさや保存タープを決める。一旦決まったら、変数値は変更できるが、変数型は変更できない。型変換やポインタ操作によって、様々な方法で変数値を変更できるが、これは変数型を変更することを意味するものではない。
###1.1 定義
varを用いて変数を定義する。但しC言語などと違い、変数型は変数名の後に置かれる。実行時、変数の初期値は二進数の零値を渡す。明示的に初期値を提供すれば、変数型を省略できる。
var x int //初期値は0
var y = true //自動的に変数型をbool型と推定する
多変量を一気に定義することも可能であり、異なる初期値や型も含まれる
var x,y int //同じ型の多変量定義
var z,w ="abc",true //異なる型の初期化
でも、グループで多行の変数定義を整理することを推奨する
var (
x,y int
z,w ="abc",true
)
###1.2 短変数宣言
var以外、ショートモードで変数を定義や初期化することができる。
func main(){
x :=100
a,b :=1,"xyz"
}
でも、短変数宣言(short variable declaration)はいくつの制限がある:
・変数定義する当時に、明示的に初期化すること
・変数型を提供しないこと
・関数内部に使うこと
以下の注意事項がある
①グローバル変数を変更しようが、同名ローカル変数を変更しまった
var x = 100
func main() {
println(&x, x) //グローバル変数
x := "abc" //ローカル変数
println(&x, x)
}
出力:
0x4cc180 100 //メモリアドレスを比べると、異なる変数とわかる
0xc000029f68 abc
②短変数宣言はいつも新しい変数を定義することではなく、部分的に値を割り当てることもできる。
func main() {
x := 100
println(&x)
x, y := 200, "abc" //xは値を割り当てる、yは変数定義
println(&x, x)
println(y)
}
出力:
0xc000029f70
0xc000029f70 200 //メモリアドレスを比べ、xは同じ変数
abc
③このような値を割り当てるのは、前提条件が少なくとも、一つの新変数を定義され、かつ同じ作用領域であること
func main() {
x := 100
println(&x)
x := 200 //エラー:no new variables on left side of :=
println(&x, x)
}
func main() {
x := 100
println(&x)
{
x, y := 200, 300 //異なる作用領域、これは新変数を定期すること
println(&x, x, y)
}
}
出力:
0xc000029f70
0xc000029f68 200 300
###1.3 多変数の割り当て
割り当ての際、まず、右側の値を計算した後、相次いで割り当てる。
package main
func main() {
x, y := 1, 2
x, y = y+3, x+2
println(x, y)
}
出力
>go run main.go
5 3
>go tool compile -N -l main.go
>go tool objdump -S main.o
TEXT %22%22.main(SB) gofile..C:/Users/tyous/Desktop/MYWORK/main.go
func main() {
0x30c 65488b0c2528000000 MOVQ GS:0x28, CX
0x315 488b8900000000 MOVQ 0(CX), CX [3:7]R_TLS_LE
0x31c 483b6110 CMPQ 0x10(CX), SP
0x320 0f8685000000 JBE 0x3ab
0x326 4883ec30 SUBQ $0x30, SP
0x32a 48896c2428 MOVQ BP, 0x28(SP)
0x32f 488d6c2428 LEAQ 0x28(SP), BP
x, y := 1, 2
0x334 48c744241001000000 MOVQ $0x1, 0x10(SP)
0x33d 48c744240802000000 MOVQ $0x2, 0x8(SP)
x, y = y+3, x+2
0x346 48c744242005000000 MOVQ $0x5, 0x20(SP)
0x34f 488b442410 MOVQ 0x10(SP), AX
0x354 4883c002 ADDQ $0x2, AX
0x358 4889442418 MOVQ AX, 0x18(SP)
0x35d 488b442420 MOVQ 0x20(SP), AX
0x362 4889442410 MOVQ AX, 0x10(SP)
0x367 488b442418 MOVQ 0x18(SP), AX
0x36c 4889442408 MOVQ AX, 0x8(SP)
println(x, y)
0x371 e800000000 CALL 0x376 [1:5]R_CALL:runtime.printlock
0x376 488b442410 MOVQ 0x10(SP), AX
0x37b 48890424 MOVQ AX, 0(SP)
0x37f e800000000 CALL 0x384 [1:5]R_CALL:runtime.printint
0x384 e800000000 CALL 0x389 [1:5]R_CALL:runtime.printsp
0x389 488b442408 MOVQ 0x8(SP), AX
0x38e 48890424 MOVQ AX, 0(SP)
0x392 e800000000 CALL 0x397 [1:5]R_CALL:runtime.printint
0x397 e800000000 CALL 0x39c [1:5]R_CALL:runtime.printnl
0x39c e800000000 CALL 0x3a1 [1:5]R_CALL:runtime.printunlock
}
0x3a1 488b6c2428 MOVQ 0x28(SP), BP
0x3a6 4883c430 ADDQ $0x30, SP
0x3aa c3 RET
func main() {
0x3ab e800000000 CALL 0x3b0 [1:5]R_CALL:runtime.morestack_noctxt
0x3b0 e957ffffff JMP %22%22.main(SB)
アセンブリ言語とコードを比較すれば
###1.4 未使用エラー
コンパイラは未使用のローカル変数をエラーとして扱う。
func main() {
x := 1
}
出力
> go run main.go
# command-line-arguments
.\main.go:4:2: x declared but not used
##2、変数名
どの言語も同じ、理解しやすい変数名を付けてでください。
###2.1 命名規則
・英文字やアンダーラインで始める。英文字、アンダーライン、数字の組み合わせ。
・大小文字を見分ける
・キャメルケース(camel case)を使用すること
・ローカル変数の短い名前の優先に使用すること
・予約語は使わないすること
・定義済みの組み込み関数、定数、型と同じ名前を使用することは推奨されない
・通常は専用名詞を大文字にするこt、例、escapeHTML
golangでは、漢字などUnicode文字はうまく使えると言っても、漢字などを変数名として使用するのは良い選択ではない。
変数の頭文字は、大文字や小文字によって作用範囲が異なる。大文字の場合、変数は外部からインポートされる。小文字の場合、変数は外部からのインポートされない、内部使うのみだ
###2.2 空識別子
pythonと同じく、golangでは、"_"(blank identifier)という特別な識別子がある。通常では、Return値を無視するために使用する。関数のReturn値を受け取るが、読み取ることができない。
import "strconv"
func main() {
x, err := strconv.Atoi("12")
println(x, err)
y, _ := strconv.Atoi("12") //AtoiのerrのReturn値を無視する
println(y)
}
出力
12 (0x0,0x0)
12
空識別子は、未使用の変数やパケットに対するコンパイラのエラー検査を一時的に回避できる。
##3 定数
const x, y int = 123, 0x22
const s = "hello 世界"
const (
i, f = 1, 0.12 //int,float64(デフォルト)
b = false
)
関数の内部に、定数を定義できる、でも、不使用の定数はエラーを起こすので、要注意。
明示的に型を指定すれば、"="左側と右側の型を一致にしなければならない。必要な場合、明示的に型変換する。対応する型の精度(有効桁数)を超えないことを要注意
const (
x, y int = 99, -999
b byte = byte(x) //xはint型に指定したので、明示的にbyte型に変換すべき
n = uint8(y) //エラー: constant -999 overflows uint8
)
定数値は、とあるコンパイラの計算できる式にしてもいける。例えば,unsafe.Sizeof,len,capなど
import "unsafe"
func main() {
const (
ptrSize = unsafe.Sizeof(uintptr(12))
strSize = len("hello 世界")
)
println(ptrSize, strSize)
}
定数グループの中、定数の型と初期値を指定しないと、それらは、前の行の空でない定数の右辺値と同じ
import "fmt"
func main() {
const (
x uint16 = 120
y // 前行xの型と値に同じ
s = "世界"
z // 前行sの型と値に同じ
)
fmt.Printf("%T,%v\n", y, y) //型と値を出力
fmt.Printf("%T,%v\n", z, z)
}
出力
uint16,120
string,世界
###3.1 列挙
goでは明示的なenumを定義していない。しかし、iotaを使って自己増加する変数値のセットを実現することによって、列挙を実現する
const (
a =iota //0
b //1
c //2
)
const (
_ = iota //0
KB = 1 << (10 * iota) // 1<<(10*1)
MB // 1<<(10*2)
GM // 1<<(10*3)
)
多変数の定義でも多数のiotaを使える、それぞれ独自に計算するので、お互いに影響を与えない。グループの中、毎行の定数の個数を一致すべきだけ
const (
_,_ = iota,iota*10 // 0,0*10
a,b //1,1*10
c,d //2,2*10
)
iotaの自分増加を中断すれば、明示的に再開すべき。その次の自分増加は行の順に増加する
const (
a = iota //0
b //1
c = 100 //100
d //100(前の行と同じ)
e = iota //4 (iotaを再開、カウントはc、dを含む)
f //5
)
時に型を指定しないと、iotaの型はintだ、でも明示的に型指定が可能
func main() {
const (
a = iota
b float32 = iota
c = iota (明示的にiotaを指定しないと、bの型と同じになる)
)
println(a)
println(b)
println(c)
}
出力
0
+1.000000e+000
2
実際の作業では、ユーザ定義型で明白な使い道のある列挙型を実現することは推奨される。しかし、これは値の範囲を予め設定した列挙の範囲内に限定されることができない
package main
type fruit byte
const (
apple fruit = iota
banana
orange
)
func eat(f fruit) {
println(f)
}
func main() {
eat(banana)
eat(100) // intの範囲は fruit/byteに超えていない
egg := 2
eat(egg) //エラー: cannot use egg (type int) as type fruit in argument to eat
}
###3.2 展開
定数はメモリアドレスがない。
package main
var x = 100
const y = 100
func main() {
println(&x, x)
println(&y, y) //cannot take the address of y
}
定数は前処理の段階で命令として展開し、使用する
package main
const y = 0x200
func main() {
println(y)
}
出力
PS C:\MYWORK> go tool compile -N -l main.go
PS C:\MYWORK> go tool objdump -S main.o
TEXT %22%22.main(SB) gofile..C:/main.go
func main() {
0x2e1 65488b0c2528000000 MOVQ GS:0x28, CX
0x2ea 488b8900000000 MOVQ 0(CX), CX [3:7]R_TLS_LE
0x2f1 483b6110 CMPQ 0x10(CX), SP
0x2f5 7634 JBE 0x32b
0x2f7 4883ec10 SUBQ $0x10, SP
0x2fb 48896c2408 MOVQ BP, 0x8(SP)
0x300 488d6c2408 LEAQ 0x8(SP), BP
println(y)
0x305 e800000000 CALL 0x30a [1:5]R_CALL:runtime.printlock
0x30a 48c7042400020000 MOVQ $0x200, 0(SP) // 定数を命令として展開
0x312 e800000000 CALL 0x317 [1:5]R_CALL:runtime.printint
0x317 e800000000 CALL 0x31c [1:5]R_CALL:runtime.printnl
0x31c e800000000 CALL 0x321 [1:5]R_CALL:runtime.printunlock
}
0x321 488b6c2408 MOVQ 0x8(SP), BP
0x326 4883c410 ADDQ $0x10, SP
0x32a c3 RET
func main() {
0x32b e800000000 CALL 0x330 [1:5]R_CALL:runtime.morestack_noctxt
0x330 ebaf JMP %22%22.main(SB)