Skip to content

Commit c5fe388

Browse files
authored
Merge pull request #950 from devlights/add-unsafe-example
Add unsafes/unsafe_add.go
2 parents 53edad7 + fd2fbf5 commit c5fe388

File tree

4 files changed

+133
-0
lines changed

4 files changed

+133
-0
lines changed

examples/basic/unsafes/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@
88
| unsafe_string.go | unsafe_string | unsafe.String() のサンプルです. |
99
| unsafe_stringdata.go | unsafe_stringdata | unsafe.StringData() のサンプルです. |
1010
| unsafe_pointer_cast.go | unsafe_pointer_cast | unsafeパッケージを用いてポインタを任意の型にキャストするサンプルです |
11+
| unsafe_add.go | usnafe_add | unsafe.Add関数を利用してポインタ演算するサンプルです |

examples/basic/unsafes/examples.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ func (r *register) Regist(m mapping.ExampleMapping) {
1717
m["unsafe_string"] = UnsafeString
1818
m["unsafe_stringdata"] = UnsafeStringData
1919
m["unsafe_pointer_cast"] = PointerCast
20+
m["unsafe_add"] = Add
2021
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package unsafes
2+
3+
import (
4+
"fmt"
5+
"unsafe"
6+
)
7+
8+
// Add は、unsafe.Add関数を利用してポインタ演算するサンプルです。
9+
//
10+
// unsafe.Addは、Go 1.17で追加された関数。
11+
// それまでは unsafe.Pointer(uintptr(ptr) + uintptr(len)) という形で行っていたポインタ演算を
12+
// 内部で行ってくれるヘルパー関数。
13+
//
14+
// これを利用することで Go でも ポインタ演算 出来るようになる。
15+
// ただし、unsafeパッケージを利用する時点でGoの持つ安全性を無くすことに注意が必要。
16+
//
17+
// REFERENCES:
18+
// - https://pkg.go.dev/unsafe@go1.25.3#Add
19+
func Add() error {
20+
var (
21+
// 0x01020304 をリトルエンディアン環境で格納すると
22+
// メモリ上は [04][03][02][01] の順に配置される
23+
value int = 0x01020304
24+
ptr = unsafe.Pointer(&value)
25+
bytePtr = (*byte)(ptr)
26+
sizeOfByte = unsafe.Sizeof((byte)(0))
27+
)
28+
29+
//
30+
// --- byte単位でのアクセス (1バイトずつ読み取り) ---
31+
//
32+
33+
// 先頭バイト(最下位バイト)を表示
34+
// リトルエンディアンなので 0x04 が格納されている
35+
fmt.Printf("1バイト目: 0x%02X\n", *bytePtr)
36+
37+
// アドレスを (1 * sizeof(byte)) 分進める (Go 1.17までのやり方)
38+
// uintptrに変換してからオフセット加算し、再度unsafe.Pointerに戻す
39+
//
40+
// (重要)
41+
// この方法で処理する場合は、ポインタ演算部分は1ステートメントで行うことが必須。
42+
// uintptrは単なる整数型であり、GCはuintptrが指すオブジェクトを追跡しない。
43+
// そのため、uintptr型の値を変数に保持したまま別の処理を挟むと、
44+
// その間にGCが実行され、元のオブジェクトが移動または解放される可能性がある。
45+
//
46+
// NG:
47+
//
48+
// tmp := uintptr(ptr) + offset // この時点でGCが入る可能性
49+
// newPtr := unsafe.Pointer(tmp) // tmpが指すアドレスは既に無効かもしれない
50+
//
51+
// OK:
52+
//
53+
// newPtr := unsafe.Pointer(uintptr(ptr) + offset) // 1ステートメントで完結
54+
//
55+
ptr = unsafe.Pointer(uintptr(ptr) + sizeOfByte)
56+
bytePtr = (*byte)(ptr)
57+
58+
// 2バイト目を表示 (0x03)
59+
fmt.Printf("2バイト目: 0x%02X\n", *bytePtr)
60+
61+
// アドレスを (2 * sizeof(byte)) 分進める (Go 1.17以降のやり方)
62+
//
63+
// unsafe.Add を使うことで、より簡潔にポインタ演算が可能
64+
// (unsafe.Addは内部で1ステートメントとして処理される)
65+
//
66+
// 元の位置から2バイト進めるので、4バイト目(0x01)にアクセス
67+
ptr = unsafe.Add(ptr, uintptr(2*sizeOfByte))
68+
bytePtr = (*byte)(ptr)
69+
70+
// 4バイト目(最上位バイト)を表示 (0x01)
71+
fmt.Printf("4バイト目: 0x%02X\n", *bytePtr)
72+
73+
//
74+
// --- int16単位でのアクセス (2バイトずつ読み取り) ---
75+
// キャスト先を *byte から *int16 へ変更
76+
//
77+
78+
var (
79+
ptr2 = unsafe.Pointer(&value) // 先頭アドレスを再取得
80+
shortPtr = (*int16)(ptr2)
81+
sizeOfShort = unsafe.Sizeof((int16(0)))
82+
)
83+
84+
// 最初の2バイト [04][03] をint16として読み取り
85+
// リトルエンディアンなので 0x0304 と解釈される
86+
fmt.Printf("short: 0x%04X\n", *shortPtr)
87+
88+
// 2バイト(1 * sizeof(short))進めて、次の2バイト [02][01] をint16として読み取り
89+
// リトルエンディアンなので 0x0102 と解釈される
90+
shortPtr = (*int16)(unsafe.Add(ptr2, sizeOfShort))
91+
fmt.Printf("short: 0x%04X\n", *shortPtr)
92+
93+
return nil
94+
95+
/*
96+
$ task
97+
task: [build] go build -o "/home/dev/dev/github/try-golang/try-golang" .
98+
task: [run] ./try-golang -onetime
99+
100+
ENTER EXAMPLE NAME: unsafe_add
101+
102+
[Name] "unsafe_add"
103+
1バイト目: 0x04
104+
2バイト目: 0x03
105+
4バイト目: 0x01
106+
short: 0x0304
107+
short: 0x0102
108+
109+
[Elapsed] 12.542µs
110+
*/
111+
}

examples/basic/unsafes/unsafe_pointer_cast.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,24 @@ func PointerCast() error {
4343
}
4444

4545
return nil
46+
47+
/*
48+
$ task
49+
task: [build] go build -o "/home/dev/dev/github/try-golang/try-golang" .
50+
task: [run] ./try-golang -onetime
51+
52+
ENTER EXAMPLE NAME: unsafe_pointer
53+
54+
[Name] "unsafe_pointer_cast"
55+
cast1: 1バイト目: 0x04
56+
---------------------------------
57+
cast2: 0バイト目: 0x04
58+
cast2: 1バイト目: 0x03
59+
cast2: 2バイト目: 0x02
60+
cast2: 3バイト目: 0x01
61+
62+
63+
[Elapsed] 42.422µs
64+
*/
65+
4666
}

0 commit comments

Comments
 (0)