Golang基础进阶——指针
变量和内存地址
每个变量都有内存地址,可以理解为变量来操作对应的内存,go语言的取地址符是&,放到一个变量前使用就会返回相应变量的内存地址,&符跟指针类型息息相关:
func main() { var a int = 10 fmt.Printf("变量地址:%x ", &a) }
指针类型介绍
指针类型,变量存的是一个地址,这个地址存的才是值
获取指针类型所指向的值,使用 *。示例:
func main(){ var a int var p *int // 指针只能存储地址否则报错, &a表示取a的地址存储到p p = &a // p存储的是a的内存地址,取出a地址指向的值就得需要加*,才能从地址中取值 fmp.Println(*p) fmp.Println(p) }
值类型和引用类型
- 值类型:int、float、bool、array、struct、string这些类型都属于值类型,使用这些类型的变量直接指向存在内存中的值,值类型的变量的值存储在栈中。当使用等号=将一个变量的值赋给另一个变量时,如 j = i ,实际上是在内存中将 i 的值进行了拷贝。特点:变量直接存储值,内存通常在栈中分配,栈在函数调用完会被释放
- 引用类型:特指指针、slice、map、channel这三种预定义类型。引用类型拥有更复杂的存储结构:(1)分配内存 (2)初始化一系列属性等一个引用类型的变量r1存储的是r1的值所在的内存地址(数字),或内存地址中第一个字所在的位置,这个内存地址被称之为指针,这个指针实际上也被存在另外的某一个字中。特点:变量存储的是一个地址,这个地址存储最终的值。内存通常在堆上分配,通过GC回收。
var 指针变量名 *指针类型
示例:
func main() { var a int = 20 var ip *int // 指针存地址 ip = &a // %x 以十进制方式表示 fmt.Printf("a的地址:%x ", &a) fmt.Printf("ip变量的值:%x ", ip) fmt.Printf("ip指向的变量的值:%d ", *ip) }
空指针
当一个指针被定义后没有分配到任何变量时,它的值为 nil
func main() { var p *int fmt.Println(p) fmt.Printf("p的值 %x ", p) }
值传递和引用传递
示例1:
func swap(a, b *int) { fmt.Println(a, b) *a, *b = *b, *a } func main() { a, b := 3, 4 swap(&a, &b) fmt.Println(a, b) }
示例2:
func swap(a, b *int) (*int, *int) { a, b = b, a return a, b } func main() { a, b := 3, 4 c, d := swap(&a, &b) fmt.Println(*c, *d) a = *c // *d 指向 a,a更改为4,所以*d也更改为4 b = *d fmt.Println(a, b) }
new()和make()
- make()用来分配引用类型的内存,例如slice、map、channel,并且初始化内存
- new()用来分配各种类型的内存,但它不会初始化内存,创建之后是一个指针类型
- make()的用途不同于new(),它只能创建slice、map、channel,并返回类型为T(非指针)的已初始化(非零值)的值
示例:
func main() { p := new(int) fmt.Println(p) // 0xc000054080 fmt.Println(*p) // 0 fmt.Printf("%T ", p) // *int m := make([]int, 10, 50) fmt.Println(m) }