Skip to content

Commit 649dc3f

Browse files
authored
Merge pull request #952 from devlights/add-unsafe-offsetof-example
Add unsafe_offsetof.go, unsafe_dump.go
2 parents bbb20b3 + aac22c0 commit 649dc3f

File tree

4 files changed

+149
-3
lines changed

4 files changed

+149
-3
lines changed

examples/basic/unsafes/README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
| file | example name | note |
66
| ---------------------- | ------------------- | -------------------------------------------------------------------- |
7-
| unsafe_sizeof.go | unsafe_sizeof | unsafe.Sizeof() についてのサンプルです. |
87
| unsafe_string.go | unsafe_string | unsafe.String() のサンプルです. |
98
| unsafe_stringdata.go | unsafe_stringdata | unsafe.StringData() のサンプルです. |
109
| unsafe_pointer_cast.go | unsafe_pointer_cast | unsafeパッケージを用いてポインタを任意の型にキャストするサンプルです |
11-
| unsafe_add.go | usnafe_add | unsafe.Add関数を利用してポインタ演算するサンプルです |
12-
| unsafe_slice.go | usnafe_slice | unsafe.SliceData() と unsafe.Slice() のサンプルです |
10+
| unsafe_add.go | unsafe_add | unsafe.Add関数を利用してポインタ演算するサンプルです |
11+
| unsafe_slice.go | unsafe_slice | unsafe.SliceData() と unsafe.Slice() のサンプルです |
12+
| unsafe_sizeof.go | unsafe_sizeof | unsafe.Sizeof() についてのサンプルです. |
13+
| unsafe_offsetof.go | unsafe_offsetof | unsafe.Offsetof() のサンプルです |
14+
| unsafe_dump.go | unsafe_dump | unsafeパッケージを使って構造体のメモリダンプを出力するサンプルです |

examples/basic/unsafes/examples.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@ func (r *register) Regist(m mapping.ExampleMapping) {
1919
m["unsafe_pointer_cast"] = PointerCast
2020
m["unsafe_add"] = Add
2121
m["unsafe_slice"] = Slice
22+
m["unsafe_offsetof"] = Offsetof
23+
m["unsafe_dump"] = Dump
2224
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package unsafes
2+
3+
import (
4+
"encoding/hex"
5+
"fmt"
6+
"math"
7+
"os"
8+
"unsafe"
9+
)
10+
11+
// Dump は、unsafeパッケージを使って構造体のメモリダンプを出力するサンプルです。
12+
func Dump() error {
13+
type (
14+
// わざとパディングが入る構造体とする
15+
// size: 24bytes
16+
S1 struct {
17+
A uint8 // offset=0, size=1, padding=3
18+
B uint32 // offset=4, size=4, padding=0
19+
C uint64 // offset=8, size=8, padding=0
20+
D uint16 // offset=16, size=2, padding=6
21+
}
22+
)
23+
24+
// 構造体のメモリダンプ
25+
// 尚、実行すると以下のように
26+
// 00000000 ff 62 a6 00 ff ff ff ff ff ff ff ff ff ff ff ff
27+
// 00000010 ff ff 1e 00 c0 00 00 00
28+
// 62 a6 のようなゴミが見える。これはGoはパディング部分をゼロ初期化しないため。
29+
// Goの仕様では、構造体のフィールドは初期化されるが、アライメントのためのパディング領域は未定義となっている。
30+
var (
31+
s1 = S1{math.MaxUint8, math.MaxUint32, math.MaxUint64, math.MaxUint16}
32+
size = unsafe.Sizeof(s1)
33+
ptr = unsafe.Pointer(&s1)
34+
bytePtr = (*byte)(ptr)
35+
byteSlice = (([]byte)(unsafe.Slice(bytePtr, size)))
36+
)
37+
hex.Dumper(os.Stdout).Write(byteSlice)
38+
fmt.Println("")
39+
40+
// ゼロクリアしてから値を再設定して確認してみる
41+
// 以下のループはmemset(ptr, 0, size) と同じ感じ。
42+
for i := range int(size) {
43+
if i == 0 {
44+
*bytePtr = 0
45+
}
46+
47+
ptr = unsafe.Add(ptr, 1)
48+
bytePtr = (*byte)(ptr)
49+
*bytePtr = 0
50+
}
51+
52+
// 構造体の値を設定しなおし
53+
s1.A = math.MaxUint8
54+
s1.B = math.MaxUint32
55+
s1.C = math.MaxUint64
56+
s1.D = math.MaxUint16
57+
58+
hex.Dumper(os.Stdout).Write(byteSlice)
59+
60+
return nil
61+
62+
/*
63+
$ task
64+
task: [build] go build -o "/home/dev/dev/github/try-golang/try-golang" .
65+
task: [run] ./try-golang -onetime
66+
67+
ENTER EXAMPLE NAME: unsafe_dump
68+
69+
[Name] "unsafe_dump"
70+
00000000 ff 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |................|
71+
00000010 ff ff 18 00 c0 00 00 00
72+
00000000 ff 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |................|
73+
00000010 ff ff 00 00 00 00 00 00
74+
75+
[Elapsed] 88.436µs
76+
*/
77+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package unsafes
2+
3+
import (
4+
"fmt"
5+
"math"
6+
"unsafe"
7+
)
8+
9+
// Offsetof は、unsafe.Offsetof() のサンプルです。
10+
//
11+
// > Offsetof returns the offset within the struct of the field represented by x, which must be of the form structValue.field.
12+
// > In other words, it returns the number of bytes between the start of the struct and the start of the field.
13+
// > The return value of Offsetof is a Go constant if the type of the argument x does not have variable size.
14+
//
15+
// > Offsetof は、引数 x によって表される構造体内のフィールドのオフセットを返します。引数 x は、必ず structValue.field の形式でなければなりません。
16+
// > 言い換えると、これは構造体の先頭からそのフィールドの先頭までのバイト数を返します。
17+
// > Offsetof の戻り値は、引数 x の型が可変サイズを持たない場合、Goの定数になります。
18+
//
19+
// REFERENCES:
20+
// - https://pkg.go.dev/unsafe@go1.25.3#Offsetof
21+
func Offsetof() error {
22+
type (
23+
// わざとパディングが入る構造体とする
24+
// size: 24bytes
25+
S1 struct {
26+
A uint8 // offset=0, size=1, padding=3
27+
B uint32 // offset=4, size=4, padding=0
28+
C uint64 // offset=8, size=8, padding=0
29+
D uint16 // offset=16, size=2, padding=6
30+
}
31+
)
32+
var (
33+
s1 = S1{math.MaxUint8, math.MaxUint32, math.MaxUint64, math.MaxUint16}
34+
off1 = unsafe.Offsetof(s1.A)
35+
off2 = unsafe.Offsetof(s1.B)
36+
off3 = unsafe.Offsetof(s1.C)
37+
off4 = unsafe.Offsetof(s1.D)
38+
size = unsafe.Sizeof(s1)
39+
)
40+
fmt.Printf("S1.A: %d\n", off1)
41+
fmt.Printf("S1.B: %d\n", off2)
42+
fmt.Printf("S1.C: %d\n", off3)
43+
fmt.Printf("S1.D: %d\n", off4)
44+
fmt.Printf("size: %d\n", size)
45+
46+
return nil
47+
48+
/*
49+
$ task
50+
task: [build] go build -o "/home/dev/dev/github/try-golang/try-golang" .
51+
task: [run] ./try-golang -onetime
52+
53+
ENTER EXAMPLE NAME: unsafe_offsetof
54+
55+
[Name] "unsafe_offsetof"
56+
S1.A: 0
57+
S1.B: 4
58+
S1.C: 8
59+
S1.D: 16
60+
size: 24
61+
62+
63+
[Elapsed] 11.773µs
64+
*/
65+
}

0 commit comments

Comments
 (0)