Go言語で型のサイズを取得する方法
C言語でいう所のsizeofはGo言語にあるんだろうか?🤔という所が気になったので調べてみました。
早速結論ですが、Go言語で型のサイズを取得する場合はunsafe.Sizeof関数を使用します。
では使い方を見ていきましょう。
unsafe.Sizeof関数を使って型のサイズを取得する
使い方はとても簡単で、Sizeof関数の引数にサイズを取得したい変数を指定してあげるだけです。
1
2
3
4
|
var a int
typeSize := unsafe.Sizeof(a)
fmt.Printf("unsafe.Sizeof関数で取得したint型のサイズ:%dバイト\n", typeSize)
|
1
|
unsafe.Sizeof関数で取得したint型のサイズ:8バイト
|
実行するとint型は8バイトという結果になりました。
ですがint型のサイズは環境に依存するため、4バイトや8バイトなどに変化することに注意してください。
ちなみにSizeof関数で取得するとuintptr型となっているため、演算で使用する時に不便です。
演算で使用したい場合はint型にキャストしてあげましょう。
1
2
3
|
arraySize := 8
totalSize := int(typeSize) * arraySize
fmt.Printf("要素数%d個のint配列のサイズ:%dバイト\n", arraySize, totalSize)
|
unsafe.sizeof関数については以上となります。
他にreflectを使用した取得方法もありますのでそちらも見ていきましょう。
reflect.TypeOf関数を使って型のサイズを取得する
こちらもunsafeのSizeof関数と同様に、Typeof関数の引数にサイズを取得したい変数を指定してSize関数を呼ぶだけです。
1
2
3
4
|
var b int
typeSize = reflect.TypeOf(b).Size()
fmt.Printf("reflect.TypeOf関数で取得したint型のサイズ:%dバイト\n", typeSize)
|
1
|
reflect.TypeOf関数で取得したint型のサイズ:8バイト
|
当然ながらどちらの関数を使用しても結果は一緒になります。
ただreflectの方は記述が長くなるため、短くてスッキリ見えるunsafeの方がいいと思います。
次は色々な型のサイズを調べて、想像と違ったものをピックアップしてみましたので見ていきましょう。
string型のサイズを調べてみる
string型のサイズがどういう結果になるのか見ていきましょう。
1
2
3
4
|
var c string
typeSize = unsafe.Sizeof(c)
fmt.Printf("string型のサイズ:%dバイト\n", typeSize)
|
16バイト???🙃
何やら斜め上な結果が返ってきましたが、stringの実行時の実体は下記の構造体になっているようです。
1
2
3
4
|
type StringHeader struct {
Data uintptr
Len int
}
|
参照:Go reflect StringHeader
構造体を指定すると各メンバのサイズの合計が返ってくるため、16バイトだったというオチですね。
次はスライスのサイズを見ていきましょう。
スライスのサイズを調べてみる
string型が構造体のサイズだったので、こちらも同じでは?という推測がたちますがどうでしょうか。
1
2
3
4
|
var d []int
typeSize = unsafe.Sizeof(d)
fmt.Printf("スライスのサイズ:%dバイト\n", typeSize)
|
推測した通り構造体のサイズになっているようです。
スライスの構造体は下記のとおりです。
1
2
3
4
5
|
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
|
参照:Go reflect SliceHeader
スライスに対してsizeof関数を使うとスライスの要素数に応じたサイズが返ってくると思いがちですが、残念ながら動的なサイズではなく静的なサイズが返されます。
ここらへんを勘違いしてしまう方は結構いそうです🙄
スライスのサイズはlenとcapで取得しましょうということですね。
では次に、もう想像がついてますが一応マップのサイズも見ていきましょう。
マップのサイズを調べてみる
1
2
3
4
|
var e map[string]int
typeSize = unsafe.Sizeof(e)
fmt.Printf("マップのサイズ:%dバイト\n", typeSize)
|
想像どおりでした🙂
恐らくマップの構造体のポインタのサイズになっていますね。
マップの実装は複雑なので、興味のある方はソースコード
を見て頂ければと思います。
まとめ
いかがでしたか。
私はsizeof関数を通して、内部の実装についてより理解が深まりました。
Go言語ではあまり使う機会がないと思いますが、どういうものか理解しておくと後々役に立つこともあると思うのでぜひ色々調べてみて下さい。
プログラム全体
コピーしてGo Playground
に貼り付ければ実行できますので、実際の動作を確認してみてください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//---------------------------------------------------------
// unsafe.Sizeof関数を使って型のサイズを取得する
//---------------------------------------------------------
var a int
typeSize := unsafe.Sizeof(a)
fmt.Printf("unsafe.Sizeof関数で取得したint型のサイズ:%dバイト\n", typeSize)
// uintptr型をint型にキャスト
arraySize := 8
totalSize := int(typeSize) * arraySize
fmt.Printf("要素数%d個のint配列のサイズ:%dバイト\n", arraySize, totalSize)
//---------------------------------------------------------
// reflect.TypeOf関数を使って型のサイズを取得する
//---------------------------------------------------------
var b int
typeSize = reflect.TypeOf(b).Size()
fmt.Printf("reflect.TypeOf関数で取得したint型のサイズ:%dバイト\n", typeSize)
//---------------------------------------------------------
// string型のサイズを調べてみる
//---------------------------------------------------------
var c string
typeSize = unsafe.Sizeof(c)
fmt.Printf("string型のサイズ:%dバイト\n", typeSize)
//---------------------------------------------------------
// スライスのサイズを調べてみる
//---------------------------------------------------------
var d []int
typeSize = unsafe.Sizeof(d)
fmt.Printf("スライスのサイズ:%dバイト\n", typeSize)
//---------------------------------------------------------
// マップのサイズを調べてみる
//---------------------------------------------------------
var e map[string]int
typeSize = unsafe.Sizeof(e)
fmt.Printf("マップのサイズ:%dバイト\n", typeSize)
}
|
1
2
3
4
5
6
|
unsafe.Sizeof関数で取得したint型のサイズ:8バイト
要素数8個のint配列のサイズ:64バイト
reflect.TypeOf関数で取得したint型のサイズ:8バイト
string型のサイズ:16バイト
スライスのサイズ:24バイト
マップのサイズ:8バイト
|
実行環境
1
2
|
$ go version
go version go1.18 linux/amd64
|