变量赋值&结构访问
1. 取地址与解引用
&是取地址符号,放到一个变量前使用就会返回相应变量的内存地址。如&a*是指针运算符,一个指针变量指向了一个值的内存地址。放到一个变量前可以对指针解引用,获取指针指向的实际变量。
package main
import "fmt"
func main() {
var a int = 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a)
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip)
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip)
}
输出如下
a 变量的地址是: c0000120d0
ip 变量储存的指针地址: c0000120d0
*ip 变量的值: 20
2. 变量赋值
go中直接对结构体进行复制,会进行值拷贝。
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
p1 := Person{Name: "张三", Age: 25}
p2 := p1 // 这里是值拷贝
// 修改 p2 不会影响 p1
p2.Name = "李四"
p2.Age = 30
fmt.Println(p1) // 输出: {张三 25}
fmt.Println(p2) // 输出: {李四 30}
}
结构体的指针属性也会拷贝,但指针指向的地址是相同的
package main
import "fmt"
type Student struct {
Name string
Scores *[]int
}
func main() {
scores := []int{90, 85, 95}
s1 := Student{Name: "小明", Scores: &scores}
s2 := s1 // 结构体是值拷贝,但Scores指针指向同一个切片
(*s2.Scores)[0] = 100 // 通过s2修改切片的内容
fmt.Println(*s1.Scores) // 输出: [100 85 95]
fmt.Println(*s2.Scores) // 输出: [100 85 95]
// 打印s1和s2的地址
fmt.Printf("s1的地址: %p\n", &s1)
fmt.Printf("s2的地址: %p\n", &s2)
// 两个结构体中Scores指针本身的地址不一样,相对s1和s2的地址偏移量是固定的
fmt.Printf("s1.Scores的指针地址: %p\n", &(s1.Scores))
fmt.Printf("s2.Scores的指针地址: %p\n", &(s2.Scores))
// 两个指针实际存储(指向)的地址是一样的
fmt.Printf("s1.Scores的指针指向地址: %p\n", s1.Scores)
fmt.Printf("s2.Scores的指针指向地址: %p\n", s2.Scores)
// 修改指针指向地址内容,会同时生效
(*(s1.Scores))[0] = 1
fmt.Printf("s1.Scores数组元素: %v\n", *s1.Scores) // 输出: [1 85 95]
fmt.Printf("s2.Scores数组元素: %v\n", *s2.Scores) // 输出: [1 85 95]
// 将s1.Scores指针指向新开辟的内存,两个指针实际存储的地址不一样
s1.Scores = &[]int{5, 6, 7}
fmt.Printf("s1.Scores的指针地址: %p\n", &(s1.Scores))
fmt.Printf("s2.Scores的指针地址: %p\n", &(s2.Scores))
fmt.Printf("s1.Scores的指针指向地址: %p\n", s1.Scores)
fmt.Printf("s2.Scores的指针指向地址: %p\n", s2.Scores)
fmt.Printf("s1.Scores数组元素: %v\n", *s1.Scores) // 输出: [5 6 7]
fmt.Printf("s2.Scores数组元素: %v\n", *s2.Scores) //输出: [1 85 95]
}
输出如下
100 85 95]
[100 85 95]
s1的地址: 0xc000010030
s2的地址: 0xc000010048
s1.Scores的指针地址: 0xc000010040
s2.Scores的指针地址: 0xc000010058
s1.Scores的指针指向地址: 0xc000010018
s2.Scores的指针指向地址: 0xc000010018
s1.Scores数组元素: [1 85 95]
s2.Scores数组元素: [1 85 95]
s1.Scores的指针地址: 0xc000010040
s2.Scores的指针地址: 0xc000010058
s1.Scores的指针指向地址: 0xc0000100c0
s2.Scores的指针指向地址: 0xc000010018
s1.Scores数组元素: [5 6 7]
s2.Scores数组元素: [1 85 95]
如果我们不希望重新分配内存空间、进行值拷贝,使用两个变量引用同一块内存空间,值变化可以联动,则需要考虑使用结构体指针复制,以便进行引用传递
package main
import "fmt"
type Student struct {
Name string
Scores *[]int
}
func main() {
var p1 = Student{Name: "张三"}
var p2 *Student = &p1 // p2是指向p1的指针
p2.Name = "李四" // 自动解引用,等同于(*p2).Name = "李四"
fmt.Println(p1.Name) // 输出: {李四 25}
fmt.Println(p2.Name) // 输出: {李四 25}
}
3. 结构体元素访问
在Go语言中,结构体指针和结构体对象都可以使用点号(.)语法访问结构体成员,这是Go语言的一个语法糖设计,目的是让代码更简洁易读。
- 简化语法:如果没有这个特性,使用结构体指针时我们需要先解引用再访问成员,像这样:
(*p).field。这种写法相对繁琐且容易出错。 - 一致性体验:允许指针和值类型使用相同的访问语法,减少了开发者需要记住的特殊情况,使代码更一致。
- 自动解引用:当你使用
p.field语法(其中p是指针)时,Go 编译器会自动将其解释为(*p).field。
package main
import "fmt"
type Person struct {
Name string
}
func main() {
// 结构体对象
p1 := Person{Name: "张三"}
fmt.Println(p1.Name) // 直接用点号访问
// 结构体指针
p2 := &p1
fmt.Println(p2.Name) // 也可以用点号访问,编译器会自动转换为 (*p2).Name
fmt.Println((*p2).Name)
}
输出如下:
张三
张三
张三