概要
Tour of Go の - Arrays についてのサンプル。
Goの配列は [要素数]型名
という形で宣言します。
var ( arr [2]int )
上だと、要素数が2のintの配列を定義しています。
特殊なのが、Goでは、配列の長さ(要素数)は「型の一部」であるということです。
つまり、[2]int
と[3]int
は全く別の型になります。
最初は不便って思えるんですが、Goにはスライスという概念があるので実際にはほぼ気になりません。
その他は別に使い方として気になる点は無いのですが、別の言語経験者からすると引っかかる癖みたいなのがあります。
Goの配列のクセ
それが、Goの配列は値であるということ。
つまり、Goの配列は関数に引数としてそのまま渡した場合、配列は値なのでそれ自身がコピーされて渡ります。
なので、関数内でその配列に対して値操作を行ったとしても、呼び元の配列には影響しません。
CやC#やJava経験者からすると、この点が最初ちょっとミスしてしまうかもしれませんね。
これらの言語で、メソッドに配列を渡すと参照(Cの場合はポインタ)が値コピーされて渡っているので
メソッド内で値を操作しても呼び元の値は変化してくれています。Goで同じようにすると呼び元の配列の値は変わりません。
例でいうと
#include <stdio.h> #include <stdlib.h> #include <string.h> void p(int *ints, int size, char *message) { for (int i = 0; i < size; i++) { printf("[%s][%d] = %d\n", message, i, ints[i]); } } void update_array(int *ints) { ints[0] = 999; ints[1] = 998; } int main() { const int arr_size = 2; int arr[arr_size]; memset(arr, 0x00, sizeof(int) * arr_size); p(arr, arr_size, "before"); update_array(arr); p(arr, arr_size, "after "); return 0; }
これは実行すると以下と出ます。
[before][0] = 0 [before][1] = 0 [after ][0] = 999 [after ][1] = 998
同様にC#で
void Main() { var arr = new int[2]; arr.Dump(); UpdateArray(arr); arr.Dump(); } // Define other methods and classes here void UpdateArray(int[] ints) { ints[0] = 999; ints[1] = 998; }
実行すると
0 0 999 998
となります。
同じようにGoで
package main import ( "fmt" ) func main() { var ( arr [2]int ) fmt.Printf("[updateArray前] %v\n", arr) updateArray(arr) fmt.Printf("[updateArray後] %v\n", arr) } func updateArray(ints [2]int) { ints[0] = 999 ints[1] = 998 fmt.Printf("[updateArray中] %v\n", ints) }
を実行すると
[updateArray前] [0 0] [updateArray中] [999 998] [updateArray後] [0 0]
という風に値が変わっていません。
同じようにするには、配列をポインタで渡す必要があります。
func updateArray2(ints *[2]int) { // 本来、ポインタでパラメータが渡されているので // 値にアクセスするには、まずデリファレンスが必要となる // (*ints)[0] = 999 // しかし、Goのランタイム側がこれを吸収してくれるので // 普通にデリファレンス無しで配列操作のように書くことができる ints[0] = 999 ints[1] = 998 fmt.Printf("[updateArray2中] %v\n", ints) }
こうすると、ちゃんと呼び元も変わります。
他の言語に慣れていると、よく引っかかりますのでご注意を。
サンプル
package tutorial import "fmt" // Array は、 Tour of Go - Arrays (https://tour.golang.org/moretypes/6) の サンプルです。 func Array() error { // ------------------------------------------------------------ // Go言語の配列 // Go言語の配列は、 [要素数]型名 という形で宣言する. // 特殊なのが、配列の長さ(要素数)は「型の一部」であるということ。 // なので、 例えば 同じ int の配列であっても // - [2]int // - [3]int // は異なる型となる。これは不便だと最初思えるが、Go言語にはスライスという // 概念があるので、実際には気にならない. // // スライスは「可変長」で、配列は「固定長」である. // // C言語経験者だと、引っかかってしまう点が一つあり // Goの配列は、値であるので関数に引数として配列を渡した場合 // 関数内で引数の配列の要素に対して値更新をしても、呼び出し元の // 配列には影響しないという点がある。同じようにするには配列のポインタを // 渡すようにする。 // // C#などの経験者も、配列を関数の引数で渡す場合、頭では参照が渡っていると // 認識してしまうので注意が必要。同じような動きをさせる場合はポインタで渡すこと。 // ------------------------------------------------------------ var ( arr [2]int ) // 初期値は設定していない場合、その要素型のゼロ値となる fmt.Println(arr) // 配列の長さは 組み込み関数 len で取れる fmt.Println(len(arr)) // Cの様に配列をループする場合は以下のようにする for i := 0; i < len(arr); i++ { fmt.Printf("%v\t", arr[i]) } fmt.Println("") // foreach ループする場合は以下のようにする for _, v := range arr { fmt.Printf("%v\t", v) } fmt.Println("") // 値の設定も他の言語と同じ arr[0] = 100 arr[1] = 200 for _, v := range arr { fmt.Printf("%v\t", v) } fmt.Println("") // Goの配列は値なので、そのまま渡すと配列自体がコピーされて渡る。 // そのため、関数内で配列の値を編集しても呼び元には影響しない。 fmt.Printf("[updateArray前] %v\n", arr) updateArray(arr) fmt.Printf("[updateArray後] %v\n", arr) fmt.Println("") // ポインタで渡すと望んだ動きとなる fmt.Printf("[updateArray2前] %v\n", arr) updateArray2(&arr) fmt.Printf("[updateArray2後] %v\n", arr) return nil } func updateArray2(ints *[2]int) { // 本来、ポインタでパラメータが渡されているので // 値にアクセスするには、まずデリファレンスが必要となる // (*ints)[0] = 999 // しかし、Goのランタイム側がこれを吸収してくれるので // 普通にデリファレンス無しで配列操作のように書くことができる ints[0] = 999 ints[1] = 998 fmt.Printf("[updateArray2中] %v\n", ints) } func updateArray(ints [2]int) { ints[0] = 999 ints[1] = 998 fmt.Printf("[updateArray中] %v\n", ints) }
try-golang/tutorial_gotour_15_array.go at master · devlights/try-golang · GitHub
実行すると以下な感じ。
[Name] "tutorial_gotour_array" [0 0] 2 0 0 0 0 100 200 [updateArray前] [100 200] [updateArray中] [999 998] [updateArray後] [100 200] [updateArray2前] [100 200] [updateArray2中] &[999 998] [updateArray2後] [999 998]
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場