- &后跟一个变量名,得到的是这个变量的内存地址
*int
类型的变量,代表这个变量里存的值是int类型的变量的内存地址- 数据类型的指针类型,即在其前面加
*
号- 指针就是内存地址
package main import( "fmt" ) func main(){ var age int = 18 //&符号+变量 就可以获取这个变量内存的地址 fmt.Println(&age) //0xc0000a2058 //ptr是一个变量,自身也有内存地址 //&age就是一个地址,是ptr变量的具体的值 var ptr *int = &age //这样直接输出,是ptr这个指针变量的值,即0xc0000a2058 fmt.Println(ptr) //ptr这个指针变量自身的地址 fmt.Println("ptr本身这个存储空间的地址为:",&ptr) //想获取ptr这个指针或者这个地址指向的那个数据: fmt.Printf("ptr指向的数值为:%v",*ptr) //ptr指向的数值为:18 }
- 对指针类型的变量再加
*
,是取真实值,即解引用
x := 10 a := &x // 取变量x的地址,a是一个指向int的指针 (*int 类型) fmt.Println(*a) // 输出a指向的整数值,即变量x的值,这里将输出 10
*
虽然可以取到指针类型的真实值(解引用),但对nil解引用,会空指针:panic: runtime error: invalid memory address or nil pointer dereference
比如以下情况:
var a *int fmt.Println(*a) // 这里将会导致空指针错误
var a *int = nil fmt.Println(*a) // 这里将会导致空指针错误
func returnNilPointer() *int { return nil } func main() { var a *int = returnNilPointer() fmt.Println(*a) // 这里将会导致空指针错误 }
对指针类型解引用的正确做法是,先判空:
var ptr *int if ptr != nil { fmt.Println(*ptr) // 安全地解引用ptr } else { fmt.Println("Pointer is nil") // 避免解引用nil指针 }
源码:
==
操作符来比较指针、切片、映射、通道和接口变量是否为 nil// 指针 var ptr *int fmt.Println(ptr) // 输出: nil,即不指向任何有效的内存地址
// 接口 var iface fmt.Stringer fmt.Println(iface == nil) // 输出: true,即接口变量不指向任何具体的实现类对象
// 切片 var s []int fmt.Println(s == nil) // 输出: true,即表示一个空切片,即长度和容量都为0的切片
// 映射 var m map[string]int fmt.Println(m == nil) // 输出: true,表示一个空映射,即不包含任何键值对的映射
// 通道 var ch chan int fmt.Println(ch == nil) // 输出: true,即未初始化的通道默认为 nil
// 定义一个函数类型 HandlerFunc type HandlerFunc func(int) string // 声明一个 HandlerFunc 类型的变量 handler,但未赋值,其值为nil var handler HandlerFunc
var ch chan int ch <- 1 // 尝试向空通道发送数据会导致panic
var m map[string]int m["key"] = 1 // 尝试在空映射中存储值会导致panic
var s []int s[0] = 1 // 越界panic
var s []int s = append(s, 1) // 不会panic或者空指针
什么时候用值传递,什么时候用指针传递?比如向函数调用栈里的下一个方法传递对象A,二者的区别在于,指针传递,传的是对象A的内存地址,传的是一个小巧的地址。值传递,是复制对象A的数据传下去。
之前有个说法:想在调用的函数内部改变对象A的值,就用指针传递,但这句话也不全对,因为用值传递,照样可以实现同样的效果。比如对象A为:
// 矩形 type Rectangle struct { Width, Height int }
此时,要改变矩形的宽,值传递和引用传递(指针传递)的实现如下:
注意看二者的返回值,值传递,因为不能直接修改原对象,因此,需要将副本对象整个都返回,引用传递,则是一个void方法,因为其接收一个原对象的内存地址,可以直接修改原对象。这两种实现方式,在此时,没有谁优谁劣。
考虑优先使用值传递,原因如下:
对于固定大小的类型(整数、浮点数、小型结构体、小型数组),它们占用的内存大小固定且小,大小与指针大小相当
值传递,代表的意志是:函数收到的是一个副本数据,我只是需要操作这份数据,不会改动你的原始数据
此外,从底层分析原因:
最后,如果传递的是一个很大的结构体,那用指针传递更优。
关于使用指针类型的场景,还有:insert数据时,数据库中默认值不对的时候:
=====
最后一篇博客,部门被裁,中午通知,下午走人。纪念人生第一次失业哈哈哈哈哈哈哈哈。
2024年8月2号 12:27