Golang的高级数据类型-指针(Pointer)实战篇
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
前面分享过存储数据的方式,可以通过变量,或者复合类型中的数组,切片,Map,结构体等进行存储。今天我们来分享一下Go语言中的指针操作。
一.变量地址和指针
1>.定义指针变量
package main import ( "fmt" ) func main() { /** 在Go语言中,一旦定义了一个变量就会在内存中开辟空间。因此,在Go语言中为了避免空间的浪费,定义变量若未使用就会编译报错。 */ var age uint8 = 18 /** 我们可以使用取地址运算符("&")来获取数据的内存地址。 %p: 是一个占位符,表示输出一个十六进制地址格式。 : 表示换行符。 */ fmt.Printf("age的值是:[%d],age的内存地址是:[%p] ", age, &age) /* 接下来我们定义一个uint8类型的指针变量p1,其实指针变量也是变量,只不过指针变量指向了变量的内存地址。 */ var p1 *uint8 /* 对age变量取地址并将结果赋值给指针变量。 */ p1 = &age fmt.Println(p1) }
2>.数组和切片指针的区别
package main import ( "fmt" ) func main() { //定义一个字符串数组 var nameList [56]string fmt.Printf("nameLists数组的内存地址为%p ", &nameList) fmt.Printf("nameList数组中第一个元素的地址为%p ", &nameList[0]) fmt.Printf("nameList数组中第二个元素的地址为%p ", &nameList[1]) fmt.Printf("nameList数组中第三个元素的地址为%p ", &nameList[2]) //定义一个整型数组 var age_list [56]int fmt.Printf("age_list数组的内存地址为%p ", &age_list) fmt.Printf("age_list数组中第一个元素的地址为%p ", &age_list[0]) fmt.Printf("age_list数组中第二个元素的地址为%p ", &age_list[1]) fmt.Printf("age_list数组中第三个元素的地址为%p ", &age_list[2]) //定义一个整型切片 age_slice := make([]int, 56) /** age_slice切片变量保存的是存储切片容器的地址。他并不像数组那样直接指向数组的首元素地址,它类似于一个二级指针。 当我们使用取地址运算符对age_slice变量取地址时并不是取的切片真正存储切片容器的地址,而是存储切片容器地址变量本身的地址哟~ */ fmt.Printf("age_slice切片变量本身的内存地址为%p ", &age_slice) /** 通过下标访问第一个元素其实访问的是age_slice变量中保存的内存地址对应的下标。 */ fmt.Printf("age_slice切片中第一个元素的地址为%p ", &age_slice[0]) fmt.Printf("age_slice切片中第二个元素的地址为%p ", &age_slice[1]) fmt.Printf("age_slice切片中第三个元素的地址为%p ", &age_slice[2]) }
二.指针的使用
1>.通过指针间接修改变量的值
package main import ( "fmt" ) func main() { //使用自动推导类型创建一个age变量 age := 18 //我们将age变量的内存地址赋值给指针变量p1 p1 := &age /** 我们可以使用取值运算符("*")来获取指针变量的值。 %p: 是一个占位符,表示输出一个十六进制地址格式。 %d: 是一个占位符,表示输出一个整型。 : 表示换行符。 *: 取值运算符,可以将指针变量中的保存的数据取出来。换句话说,可以将一段内存地址保存的值取出来。 */ fmt.Printf("age的内存地址是:%p,age的值是:%d ", p1, *p1) //我们可以通过指针间接修改变量的值 *p1 = 27 fmt.Printf("age的内存地址是:%p,age的值是:%d ", p1, age) }
2>.使用指针注意事项
package main import ( "fmt" ) func main() { /** 温馨提示: 在使用指针变量时,要注意定义指针默认值为nil,如果直接擦欧总指向nil的内存地址会报错; 在其它语言中,比如c++,指针是允许运算的,但是在Go语言中,指针只有取地址运算符和取值运算符,不允许指针参与运算哟~ */ var p *int /** 错误代码: 由于p指针变量默认值是nil,因此会报错"invalid memory address or nil pointer dereference" */ //*p = 123 age := 18 p = &age //所以,在使用指针变量时,一定要让指针变量有正确的指向。 fmt.Printf("age的内存地址是:%p,age的值是:%d ", p, *p) /** 错误代码: 在Go语言中,指针只有取地址运算符和取值运算符。 */ //p2 := p + 100 }
三.指针变量赋值
package main import ( "fmt" ) func main() { /** 为指针赋值有两种方法: 1>.把同类型的变量地址赋值给指针; 2>.使用new函数,就是申请一片内存空间,返回地址; */ var ( p1 *int p2 *int ) fmt.Println("p1的默认值是:", p1) fmt.Println("p2的默认值是:", p2) /** 方案一: 定义一个同类型的变量,然后取地址赋值给指针变量。 */ age := 18 p1 = &age fmt.Printf("p1的内存地址是:%p,p1的值是:%d ", p1, *p1) /** 方案二: 使用new函数,比如"new(int)"表示创建一个int大小的内存空间,返回为*int */ p2 = new(int) fmt.Printf("p2的内存地址是:%p,p2的值是:%d ", p2, *p2) *p2 = 2020 //为指针变量赋值 fmt.Printf("p2的内存地址是:%p,p2的值是:%d ", p2, *p2) }
四.指针的应用场景(指针变量作为函数参数传递的时候是引用传递)
1>.交换两个变量的值案例
package main import "fmt" /* 定义一个函数交换2个变量的值,接收的是2个int变量 */ func swap(x int, y int) { x, y = y, x } /* 定义一个函数交换2个变量的值,接收的是2个int指针变量 */ func swapPointer(x *int, y *int) { *x, *y = *y, *x //需要改变的是地址上存储的内容 } func main() { /* 定义2个变量,一会用于在函数中交换两个变量的值 */ var ( a = 100 b = 200 ) /* 值传递,不能通过函数内部修改影响外部变量。 */ swap(a, b) fmt.Printf("a = [%d], b = [%d] ", a, b) /* 指针变量作为函数参数传递的时候是引用传递 */ swapPointer(&a, &b) fmt.Printf("a = [%d], b = [%d] ", a, b) }
2>.结构体传参案例
package main import ( "fmt" ) type Student struct { Name string Age int } func addAge(s Student) { s.Age += 1 } /** 在实际开发中,一般传递的结构体对象都是指针传递 */ func addAgePointer(s *Student) { s.Age += 2 } func main() { s1 := Student{"Jason Yin", 18} addAge(s1) fmt.Printf("姓名:[%s],年龄[%d] ", s1.Name, s1.Age) /** 结构体对象是值传递,但一般函数传递的都是结构体对象指针 */ addAgePointer(&s1) fmt.Printf("姓名:[%s],年龄[%d] ", s1.Name, s1.Age) }