指针类型
- 指针类型是
依托
某一类型而存在的
- 对于某一类型 T,以 T 为
基类型
的指针类型为 *T
unsafe.Pointer
不需要基类型,用于表示一个通用指针类型
任何指针类型
和 unsafe.Pointer
可以相互显式转换
1 2 3 4 5 6 7
| type T struct{}
func main() { var p *T var p1 = unsafe.Pointer(p) p = (*T)(p1) }
|
如果指针类型变量
没有被显式赋予初值,默认值为 nil
1 2 3 4 5 6
| type T struct{}
func main() { var p *T println(p == nil) }
|
给指针类型变量赋值
1 2 3
| var a int = 13 var p *int = &a fmt.Printf("%T, %v\n", p, *p)
|
如果类型不匹配
(且不支持显式转换
,除非用 unsafe
),编译器会报错
1 2
| var b byte = 10 var p *int = &b
|
内存分配
非指针
对于非指针类型变量
,Go 在对应的内存单元中存放的是该变量的值
指针
对于指针类型变量
,Go 在为其分配的内存单元中存储的是地址
指针类型变量的大小与基类型大小
无关,而与系统地址的表示长度
有关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| type foo struct { id string age int8 addr string }
func main() { var p1 *int var p2 *bool var p3 *byte var p4 *[20]int var p5 *foo var p6 unsafe.Pointer
println(unsafe.Sizeof(p1)) println(unsafe.Sizeof(p2)) println(unsafe.Sizeof(p3)) println(unsafe.Sizeof(p4)) println(unsafe.Sizeof(p5)) println(unsafe.Sizeof(p6)) }
|
unsafe/unsafe.go1
| func Sizeof(x ArbitraryType) uintptr
|
在 Go 中,uintptr
类型的大小就是指针类型的大小
指针操作
解引用
可以通过指针读取
或者修改
其指向的内存单元所代表的基类型变量
1 2 3 4 5
| var a = 17 var p = &a println(*p) *p += 3 println(*p)
|
地址
修改指向
1 2 3 4 5 6 7
| var a = 5 var b = 6
var p = &a println(*p) p = &b println(*p)
|
同一指向
多个指针变量可以指向同一个变量的内存单元
1 2 3 4 5 6 7
| var a = 5
var p1 = &a var p2 = &a
*p1 += 5 println(*p2)
|
二级指针
1 2 3 4 5 6 7 8 9 10 11 12 13
| a := 5 p1 := &a println(*p1)
b := 55 p2 := &b println(*p2)
pp := &p1 fmt.Printf("%T\n", pp) println(**pp) pp = &p2 println(**pp)
|
对二级指针解引用,得到的是一级指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| a := 5 p1 := &a println(*p1)
b := 55 p2 := &b println(*p2)
pp := &p1 fmt.Printf("%T\n", pp)
println(*pp == p1) println(**pp == a)
pp = &p2 println(*pp == p2) println(**pp == b)
|
场景:跨函数
改变一个指针变量的指向
1 2 3 4 5 6 7 8 9 10 11 12
| func change(pp **int) { b := 55 *pp = &b }
func main() { a := 5 p := &a println(*p) change(&p) println(*p) }
|
主要用途
- 在 Go
运行时
层面,尤其是内存管理
和垃圾回收
,只关心指针
- Go 项目一般
比较少使用
指针
- 因为 Go 提供了更灵活和高级的
复合类型
,并将使用指针的复杂性隐藏
在运行时
的实现层面
- 指针的意义在于
可修改
- 指针的
传递开销
为 O(1)
使用限制
Go 的目标之一是成为一门安全
的编程语言
显式转换
C
1 2 3 4 5 6 7 8
| #include <stdio.h>
int main() { int a = 0x12345678; int *p = &a; char *p1 = (char *) p; printf("%x\n", *p1); }
|
Go
1 2 3 4 5 6
| func main() { a := 0x12345678 pa := &a pb := (*byte)(pa) fmt.Printf("0x%x\n", *pb) }
|
unsafe
编程需要开发者自己保证内存安全
1 2 3 4 5 6
| func main() { a := 0x12345678 pa := &a pb := (*byte)(unsafe.Pointer(pa)) fmt.Printf("0x%x\n", *pb) }
|
指针运算
C
1 2 3 4 5 6 7
| int main() { int a[] = {1, 2, 3, 4, 5}; int *p = &a[0]; for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++) { printf("%d\n", *(p++)); } }
|
Go
1 2 3 4 5 6 7 8
| func main() { arr := [5]int{1, 2, 3, 4, 5} p := &arr[0] println(*p) p = p + 1 p++ println(*p) }
|
unsafe
1 2 3 4 5 6 7 8 9 10 11 12 13
| func main() { arr := [5]int{1, 2, 3, 4, 5} p := &arr[0]
base := uintptr(unsafe.Pointer(p)) size := unsafe.Sizeof(arr[0])
var i uintptr for i = 0; i < uintptr(len(arr)); i++ { pi := (*int)(unsafe.Pointer(base + i*size)) println(*pi) } }
|