質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Q&A

1回答

1510閲覧

【Go】mapの変数宣言と初期化に関して / 他の型との異なる挙動?

nyako

総合スコア45

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

0グッド

1クリップ

投稿2022/09/04 07:47

mapの変数宣言と初期化に関してです。

以下の該当コードですが、2行目でアドレスを出力をするとアドレスが帰ってきます。

3行目でm3["orange"] = 150が出来ないのは、var m3 map[string]intのように変数宣言はしているもののメモリ上に確保をしていないからということで学んだのですが違うのでしょうか?

思ったのは変数宣言はメモリに変数の場所は確保されるけどstringとかと違ってmapだけは少し違うということなんですかね。

該当コード

var m3 map[string]int // 変数宣言はしているもののメモリ上に確保をしていない fmt.Printf("address: %p\n", &m3) // address: 0x140000ac020 なぜアドレスが出力されるのでしょうか m3["orange"] = 150 // 代入するmapがないので不可 fmt.Println(m3) // panic: assignment to entry in nil map

正解例

m2 := make(map[string]int) // 初期化(メモリ上に空のmapを作る) fmt.Printf("address: %p\n", &m2) // address: 0x14000122018 OK! m2["apple"] = 100 // 代入可能 fmt.Println(m2) // map["apple"]100

宜しくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

map系の型は後述の内部定義された構造体型として扱われます。
なので型宣言されただけで構造体のメモリは確保されます(スタックかヒープかは状況による)。
以下の&m3はその確保された構造体のアドレスを表示していることになります。

go

1var m3 map[string]int 2fmt.Printf("address: %p\n", &m3)

map型の内部表現

https://github.com/golang/go/blob/2580d0e08d5e9f979b943758d3c49877fb2324cb/src/runtime/map.go#L116-L130

go

1type hmap struct { 2 // Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go. 3 // Make sure this stays in sync with the compiler's definition. 4 count int // # live cells == size of map. Must be first (used by len() builtin) 5 flags uint8 6 B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items) 7 noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details 8 hash0 uint32 // hash seed 9 10 buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0. 11 oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing 12 nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated) 13 14 extra *mapextra // optional fields 15}

型宣言だけだと、buckets==nilです。bucketsポインタの指す先に別のメモリがあてがわれて初めてmap型として機能します。
「bucketsポインタの指す先に別のメモリをあてる」のがmakeやリテラルなどを記述する方法です。

go

1m1 := map[string]int{} 2m2 := make(map[string]int)

(ちなみにm1とm2は初期化の記述方法が違いますが得られるものは同等です。)
m1やm2であればマップが機能するためのヒープメモリ割り当てがkey、elem、bucketポインタにセットされているのでマップとしてアクセスすることができます。

mapの宣言と文字列の宣言が違うのか?といわれるとメモリ構造は似ています。

https://github.com/golang/go/blob/2580d0e08d5e9f979b943758d3c49877fb2324cb/src/runtime/string.go#L238-L241

go

1type stringStruct struct { 2 str unsafe.Pointer 3 len int 4}

strポインタの指す先に文字列データが格納されている必要がありますが、こちらは「文字列値」そのものがコード中にあるのでそれをコンパイラはプログラムコードに埋め込み、そこへのポインタや長さが最初から算出可能なのでstrもlenもコンパイル時点で初期値を与えられます。

つまり、文字列もスライスもマップも宣言に関する情報を持つ構造体があり、実データは別途メモリ確保してそのポインタを構造体が持っているという形です。
実データ用に別途初期化処理にてメモリ割り当てが必要です。それを行うのがmake関数やリテラル初期化による記述ということです。

投稿2022/09/04 15:05

編集2022/09/06 05:54
nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問