指针
本章围绕字符串、数字、数组、切片、map、channel、结构体与指针赋值及函数传参的应用剖析
字符串
字符串本身也是StringHeader的结构体,包含Data指针与字符串长度,如下
type StringHeader struct { Data uintptr Len int }
Data指向的内存地址不可更改,字符串赋值和传参只是拷贝了StringHeader中Data和Len的值
package main import "fmt" func main() { str := "Hello World!" var data string data = str // 拷贝str中Data与Len fmt.Println(data) data = "Hello Go!" // 修改data的值 在内存中生成新的StringHeader结构体 并赋值给data fmt.Println(data) // str内存未改变 fmt.Println(str) } //Hello World! //Hello Go! //Hello World!
当声明变量为字符串指针时,变量赋值时对象一定是字符串内存地址,函数传参时拷贝的也是内存地址而已
package main import "fmt" func main() { str := "Hello World!" var ptr *string // 错误方法 str是字符串类型非指针类型 //ptr = str // 正确方式 获取str的地址 赋值给ptr ptr = &str fmt.Println(ptr) fmt.Println(*ptr) } //0xc0000421c0 //Hello World!
数字
数字未找到对应的结构体,数字类型赋值、指针、函数传参与字符串一致,不可修改(修改等同于重新赋值)
package main import "fmt" func main() { number := 10 var ptr *int // 错误方法 str是字符串类型非指针类型 //ptr = str // 正确方式 获取str的地址 赋值给ptr ptr = &number fmt.Println(ptr) fmt.Println(*ptr) } //0xc00000a0b8 //10
数组
数组赋值、函数传参时,进行都是内存拷贝,数据都会拷贝一份,新的数组修改不影响被赋值的数组
package main import "fmt" func main() { list1 := [4] int{1,2,3,4} list2 := list1 list2[0] =100 fmt.Println(list1) fmt.Println(list2) } //[1 2 3 4] //[100 2 3 4]
数组指针,指针修改时不需要加*的;修改时,原数组更改
package main import "fmt" func main() { list := [4]int{1,2,3,4} // 声明时指定数组大小 var ptr *[4]int ptr = &list // 错误赋值 //*ptr[0] =100 // 正确方式 ptr[0] = 100 fmt.Println(list) fmt.Println(*ptr) } //[100 2 3 4] //[100 2 3 4]
切片
切片结构体 SliceHeader 如下
type SliceHeader struct { // 指向数组内存地址 赋值时拷贝的是数组地址 Data uintptr // 长度 Len int // 申请空间 Cap int }
赋值、copy、函数传参时只是拷贝了结构体中的变量。详情Go语言【数据结构】切片
package main import "fmt" func main() { slice1 := []int{1,2,3,4} slice2 := slice1 slice2[0] =100 fmt.Println(slice1) fmt.Println(slice2) } //[100 2 3 4] //[100 2 3 4]
切片指针,下面方式都不可以修改
package main import "fmt" func main() { slice1 := []int{1,2,3,4} var ptr *[]int ptr = &slice1 // 下面两种方式都不可以修改 ptr[0] =100 *ptr[0] =100 fmt.Println(slice1) fmt.Println(*ptr) } //[1 2 3 4] //[1 2 3 4]
channel
通道在赋值时,指向的都是同一块内存地址
package main import "fmt" func main() { chan1 := make(chan string,1) chan2 := chan1 chan2 <- "hello" data := <-chan1 fmt.Println(data) // 指向同一块地址 fmt.Println(chan1) fmt.Println(chan2) } //hello //0xc000088000 //0xc000088000
同时,通道也支持指针
package main import "fmt" func main() { chan1 := make(chan string,1) chan2 := &chan1 *chan2 <- "hello" data := <-chan1 fmt.Println(data) fmt.Println(chan1) fmt.Println(chan2) } //hello //0xc000038060 //0xc000006028
结构体
那么重点来了,结构体赋值和指针有何区别,先看戏结构体生成的三种方式,其中第二种方式和第三中方式一致
package main import "fmt" type Phone struct { color string name string } func main() { // 第一种生成方式 生成结构体对象 phone1 :=Phone{"Red","Iphone"} fmt.Println(phone1.color) fmt.Println(phone1.name) fmt.Println(phone1) // 第二种生成方式 生成结构体指针 phone2 :=&Phone{"Red","Iphone"} fmt.Println(phone2.color) fmt.Println(phone2.name) fmt.Println(phone2) // 第三种生成方式 生成结构体指针 phone3 := new(Phone) phone3.color = "Red" phone3.name = "Iphone" fmt.Println(phone3.color) fmt.Println(phone3.name) fmt.Println(phone3) } //Red //Iphone //{Red Iphone} //Red //Iphone //&{Red Iphone} //Red //Iphone //&{Red Iphone}
结构体赋值,等同于拷贝了结构体中的变量,函数传参与赋值一样
package main import "fmt" type Phone struct { color string name string } func main() { // 赋值 phone1 :=Phone{"Red","Iphone"} phone2 := phone1 phone2.color = "Green" fmt.Println(phone1.color) fmt.Println(phone2.color) } //Red //Green
而指针只是拷贝了结构体的内存地址,修改会影响原来的值
package main import "fmt" type Phone struct { color string name string } func main() { // 赋值 phone1 :=&Phone{"Red","Iphone"} phone2 := phone1 phone2.color = "Green" fmt.Println(phone1.color) fmt.Println(phone2.color) } //Green //Green