1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

GO言語学習ノート#1  変数型(1)

Last updated at Posted at 2020-04-26

環境
Windows10
Visual Studio Code (Go for Visual Studio Code,Code Runner)
golang 1.14

##1、変数
  静的型付き言語として、go言語はいつも固定的なデータ型を持ち、データ型は変数のメモリ領域上の大きさや保存タープを決める。一旦決まったら、変数値は変更できるが、変数型は変更できない。型変換やポインタ操作によって、様々な方法で変数値を変更できるが、これは変数型を変更することを意味するものではない

###1.1 定義
  varを用いて変数を定義する。但しC言語などと違い、変数型は変数名の後に置かれる。実行時、変数の初期値は二進数の零値を渡す。明示的に初期値を提供すれば、変数型を省略できる。

main.go
var x int      //初期値は0
var y = true  //自動的に変数型をbool型と推定する

多変量を一気に定義することも可能であり、異なる初期値や型も含まれる

main.go
var x,y int            //同じ型の多変量定義
var z,w ="abc",true   //異なる型の初期化

でも、グループで多行の変数定義を整理することを推奨する

main.go
var (
     x,y int
     z,w ="abc",true
)

###1.2 短変数宣言
  var以外、ショートモードで変数を定義や初期化することができる。

main.go
func main(){
	x :=100
	a,b :=1,"xyz"
}

でも、短変数宣言(short variable declaration)はいくつの制限がある:
・変数定義する当時に、明示的に初期化すること
・変数型を提供しないこと
・関数内部に使うこと

以下の注意事項がある
①グローバル変数を変更しようが、同名ローカル変数を変更しまった

main.go
var x = 100

func main() {
	println(&x, x) //グローバル変数

	x := "abc"     //ローカル変数
	println(&x, x)
}

出力:

0x4cc180     100        //メモリアドレスを比べると、異なる変数とわかる
0xc000029f68   abc

②短変数宣言はいつも新しい変数を定義することではなく、部分的に値を割り当てることもできる。

main.go
func main() {
	x := 100
	println(&x)

	x, y := 200, "abc" //xは値を割り当てる、yは変数定義
	println(&x, x)
	println(y)
}

出力:

0xc000029f70  
0xc000029f70 200 //メモリアドレスを比べ、xは同じ変数
abc

③このような値を割り当てるのは、前提条件が少なくとも、一つの新変数を定義され、かつ同じ作用領域であること

main.go
func main() {
	x := 100
	println(&x)

	x := 200    //エラー:no new variables on left side of :=
	println(&x, x)
}
main.go
func main() {
	x := 100
	println(&x)
	{
		x, y := 200, 300    //異なる作用領域、これは新変数を定期すること
		println(&x, x, y)
	}

}

出力:

0xc000029f70
0xc000029f68 200 300

###1.3 多変数の割り当て
  割り当ての際、まず、右側の値を計算した後、相次いで割り当てる。

main.go
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 未使用エラー

コンパイラは未使用のローカル変数をエラーとして扱う。

main.go
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値を受け取るが、読み取ることができない。

main.go
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 定数

main.go
	const x, y int = 123, 0x22
	const s = "hello 世界"

	const (
		i, f = 1, 0.12  //int,float64(デフォルト)
		b    = false
	)

関数の内部に、定数を定義できる、でも、不使用の定数はエラーを起こすので、要注意。

明示的に型を指定すれば、"="左側と右側の型を一致にしなければならない。必要な場合、明示的に型変換する。対応する型の精度(有効桁数)を超えないことを要注意

main.go
	const (
		x, y int  = 99, -999  
		b    byte = byte(x)   //xはint型に指定したので、明示的にbyte型に変換すべき
		n         = uint8(y)  //エラー: constant -999 overflows uint8
	)

定数値は、とあるコンパイラの計算できる式にしてもいける。例えば,unsafe.Sizeof,len,capなど

main.go
import "unsafe"

func main() {
	const (
		ptrSize = unsafe.Sizeof(uintptr(12))
		strSize = len("hello 世界")
	)
	println(ptrSize, strSize)
}

定数グループの中、定数の型と初期値を指定しないと、それらは、前の行の空でない定数の右辺値と同じ

main.go
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を使って自己増加する変数値のセットを実現することによって、列挙を実現する

main.go
	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を使える、それぞれ独自に計算するので、お互いに影響を与えない。グループの中、毎行の定数の個数を一致すべきだけ

main.go
	const (
		_,_  = iota,iota*10  // 0,0*10
		a,b                  //1,1*10
		c,d                  //2,2*10
	)

iotaの自分増加を中断すれば、明示的に再開すべき。その次の自分増加は行の順に増加する

main.go
	const (
		a = iota  //0
		b         //1
		c = 100	  //100
		d         //100(前の行と同じ)
		e = iota  //4 (iotaを再開、カウントはc、dを含む)
		f         //5
	)

時に型を指定しないと、iotaの型はintだ、でも明示的に型指定が可能

main.go
func main() {

	const (
		a         = iota
		b float32 = iota
		c         = iota (明示的にiotaを指定しないとbの型と同じになる)
	)
	println(a)
	println(b)
	println(c)
}

出力

0
+1.000000e+000
2

実際の作業では、ユーザ定義型で明白な使い道のある列挙型を実現することは推奨される。しかし、これは値の範囲を予め設定した列挙の範囲内に限定されることができない

main.go
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 展開
定数はメモリアドレスがない。

main.go
package main

var x = 100
const y = 100

func main() {
	println(&x, x)
	println(&y, y)    //cannot take the address of y

}

定数は前処理の段階で命令として展開し、使用する

main.go
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)
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?