Go Pointer

指针

指针是什么?

指针是一个变量,指针保存了一个变量的地址 ,  所以说指针指向变量 , 可以通过指针找到变量。

指针-内存的储存方式

func main() {
	var x int
	var y bool 
	px := &x
	py := &y
	var z string
	var pz *string
	fmt.Printf("x变量的值:%v,px指针的值:%v\n", x, px)
	fmt.Printf("x变量的内存地址:%v,px指针解引用的值:%v\n", &x, *px)
	fmt.Printf("pz指针的值:%v,pz指针的地址%v", pz, &pz)
	fmt.Println(unsafe.Sizeof(x))
	fmt.Println(unsafe.Sizeof(y))
	fmt.Println(unsafe.Sizeof(z))
	fmt.Println(unsafe.Sizeof(px))
	fmt.Println(unsafe.Sizeof(py))
	fmt.Println(unsafe.Sizeof(pz))
}

8f64f5585a314ef3bf4be8f1fde02cef.jpeg

指针的占用内存?指针和变量值的关系?

在Go语言中,指针无论是什么类型指针占用的内存都是64位八个字节。
变量的地址赋值给指针,指针的值就是变量的地址,变量的值就是指针解引用。

指针-声明赋值

func main() {
	var ps *string
	s := "张三"
	ps = &s
	fmt.Println(ps, &s)
}

指针如何声明赋值?

在Go语言中,在变量前使用&取地址符返回没错地址。指针类型可以基于任何类型,在前面加*符号。

指针-初始化

func main() {
	pi := new(int)
	fmt.Println(pi, *pi)
}

指针如何初始化?

在Go语言中,指针初始化可以把变量地址赋值给变量、使用new初始化指针 , 会连带创建指针指向的变量 , 指向的变量是默认值

func main() {
	var s string
	ps := &s
	fmt.Printf("%T\n", ps)
	fmt.Println(ps, s)
}

指针-解引用

func main() {
	s := "张三"
	ps := &s
	fmt.Println(*ps)
}
func main() {
	var ps *string
	fmt.Println(ps == nil)
	fmt.Println(*ps)
}

/*
panic: runtime error: invalid memory address or nil pointer dereference
*/

指针如何解引用?

在Go语言中,指针的解引用操作对指针取*号获取指针指向的变量。

如果对未初始化的指针变量进行解引用操作会panic

 指针-赋值新变量

func main() {
	a := "张三"
	b := "李四"
	pa := &a
	pb := pa
	fmt.Printf("pa的地址:%v,pa指向变量a的地址:%v\n", &pa, pa)
	fmt.Printf("pb的地址:%v,pb指向变量b的地址:%v\n", &pb, pb)
	pb = &b
	fmt.Printf("pa的地址:%v,pa指向变量a的地址:%v\n", &pa, pa)
	fmt.Printf("pb的地址:%v,pb指向变量b的地址:%v\n", &pb, pb)
	*pb = "王五"

}

指针-比较

func main() {
	var ps *string
	fmt.Println(ps == nil)

	s := "张三"
	s1 := "李四"
	ps = &s
	ps1 := &s1
	fmt.Println(ps1 == ps)

	ps2 := &s
	fmt.Println(ps2 == ps)
}

指针的比较?

在Go语言中,未初始的指针等与nil,如果两个指针指向相同的变量则是true,指向不同则为false

指针-函数传递

/*
指针-函数传递
*/
func f(fpi *int) {
	fmt.Printf("fpi指针的地址:%v\n", &fpi)
	fmt.Printf("fpi指针的指向变量的地址(指针的值):%v\n", fpi)
	fmt.Printf("fpi指针的指向变量的值:%v\n", *fpi)
	*fpi = 1
	fmt.Printf("修改fpi指针的指向变量的值:%v\n", *fpi)
}
func main() {
	pi := new(int)
	fmt.Printf("pi指针的地址:%v\n", &pi)
	fmt.Printf("pi指针的指向变量的地址(指针的值):%v\n", pi)
	fmt.Printf("pi指针的指向变量的值:%v\n", *pi)
	f(pi)

}

/*
pi指针的地址:0xc00014c018
pi指针的指向变量的地址(指针的值):0xc000128058
pi指针的指向变量的值:0
fpi指针的地址:0xc00014c028
fpi指针的指向变量的地址(指针的值):0xc000128058
fpi指针的指向变量的值:0
修改fpi指针的指向变量的值:1
*/
func f(fpi *int) {
	fmt.Printf("fpi指针的地址:%v\n", &fpi)
	fmt.Printf("fpi指针的指向变量的地址(指针的值):%v\n", fpi)
	fmt.Printf("fpi指针的指向变量的值:%v\n", *fpi)
	i := 10
	fpi = &i
	fmt.Printf("修改后fpi指针的地址:%v\n", &fpi)
	fmt.Printf("修改后fpi指针的指向变量的地址(指针的值):%v\n", fpi)
	fmt.Printf("修改后fpi指针的指向变量的值:%v\n", *fpi)

}
func main() {
	pi := new(int)
	fmt.Printf("pi指针的地址:%v\n", &pi)
	fmt.Printf("pi指针的指向变量的地址(指针的值):%v\n", pi)
	fmt.Printf("pi指针的指向变量的值:%v\n", *pi)
	f(pi)
	fmt.Println(pi, *pi)

}

/*
fpi指针的地址:0xc000006038
fpi指针的指向变量的地址(指针的值):0xc0000160a8
fpi指针的指向变量的值:0
修改后fpi指针的地址:0xc000006038
修改后fpi指针的指向变量的地址(指针的值):0xc0000160d0
修改后fpi指针的指向变量的值:10
0xc0000160a8 0
*/

指针在函数中传递的现象?

在Go语言中,Go语言是值传递语言,传递函数的值是副本,

对于基本类型、结构体、数组等非指针类型,调用的函数不会修改原始值。

如果指针、切片、字典传递给给函数。如果对参数修改,会影响传入的变量,如果对参数中心赋值,不会影响,如果传入的参数中是nil,不会影响。

尽管当指针给传递到了函数 , 函数获得是指针的副本(也就是说,指针的地址不一致)

在函数f中的i参数fpi是指针变量pi的副本 , 所以 给fpi 赋值了一个新变量的地址 fpi指向的变量改变了, 不和pi指向的变量一样了,不会影响到 pi 指针变量

指针-创建结构体指针实例

type User struct {
	Name string
	Age  int
}

func main() {
	u := &User{}
	u1 := new(User)
	fmt.Println(u, u1)

}

结构体指针如何创建实例?

在Go语言中,可以在结构体前使用&创建指针实例 , 但无法在基本字面量前使用&

里面包含指针类型基础类型

type User struct {
	Name string
	Age  *int
}

func main() {
	u := &User{}
	fmt.Println(u.Age) // 输出:nil
	u.Age = 3
	/*
		u.Age = 3 编译错误
		cannot use 3 (untyped int constant) as *int value in assignment
		不能在赋值中使用3(未类型化int常量)作为*int值
	*/
	u.Age = &3
	/*
		u.Age = &3 编译错误
		不能对常量取地址
	*/

}
type User struct {
	Name string
	Age  *int
}

func main() {
	u := &User{}
	fmt.Println(u.Age) // 输出:nil
	var i = 10
	u.Age = &i
	fmt.Println(u.Age, *u.Age)

}
type User struct {
	Name string
	Age  *int
}

func main() {
	u := &User{}
	fmt.Println(u.Age) // 输出:nil
	u.Age = new(int)
	*u.Age = 10
	fmt.Println(u.Age, *u.Age)
}

使用辅助函数

type User struct {
	Name *string
	Age  int
}

func stingP(s string) *string {
	return &s
}
func main() {
	u := User{
		Name: stingP("张三"),
		Age:  0,
	}
	fmt.Println(u)

}

指针-作为第二选择

type User struct {
	Name string
	Age  int
}

//不推荐
func MakeUser(user *User) error {
	user.Name = "张三"
	user.Age = 5
	return nil
}
//推荐
func MakeUser1() (User, error) {
	return User{
		Name: "张三",
		Age:  5,
	}, nil
}

函数修改一个结构体最佳实践?

在Go语言中,函数修改一个结构体实例,不如直接返回结构体实例。

指针传递修改会给垃圾回收增加额外工作,因为会复制变量的指针,函数返回后会回收复制的指针,不如让函数实例化返回结构

指针-指针传递优化性能

函数中传递类型的最佳实践?

在Go语言中,函数中传递兆字节或更大数据,尽量考虑是用指针传递。

大多数情况下,使用指针和数值在函数中传递性能差异不大,但是如果在函数之间传递兆字节或者更大的数据,考虑使用指针,即使数据不会改变。

指针-空值和零值

// getStudentByID 模拟从数据库或其他数据源中根据学生 ID 获取学生信息的函数
func getStudentByID(id int) *string {
	// 模拟数据库查询
	a := "张三"
	b := "李四"
	if id == 1 {
		return &a
	} else if id == 2 {
		return &b
	} else {
		return nil
	}
}

func main() {
	// 尝试获取学生信息
	studentName := getStudentByID(1)
	fmt.Println(*studentName)

	studentName1 := getStudentByID(2)
	fmt.Println(*studentName1)

	if nil == getStudentByID(3) {
		fmt.Println("没找到学生")
	}

}
// getStudentByID 模拟从数据库或其他数据源中根据学生 ID 获取学生信息的函数
func getStudentByID(id int) (string,bool) {
	// 模拟数据库查询
	if id == 1 {
		return "张三", true
	} else if id == 2 {
		return "", true
	} else {
		return "", false
	}
}

func main() {
	// 尝试获取学生信息
	studentName, found := getStudentByID(3)

	// 判断是否找到学生
	if found {
		fmt.Println("Student found:", studentName)
	} else {
		fmt.Println("Student not found")
	}
}

如何判断零值和空值的最佳实践?

在Go语言中,尽量避免返回nil指针的情

况,建议使用映射中介绍的逗号,ok模式。nil解引用会导致panic。

指针-映射和切片的区别

映射切片和指针的关系?

在Go语言中,映射切片被实现一个指向结构的指针,将它们赋值新变量和函数中传递意味这赋值了一个新指针。

映射在函数中使用最佳实践?

在Go语言中,尤其是在公共API中应该避免映射作为输入参数和返回值。

切片在函数中使用最佳实践?

在Go语言中,把切片传递到函数中修改,不能改变它的长度和容量,但可以改变其内容。

指针-切片用作缓冲区

如何从数据源获取数据的最佳实践?

在Go语言惯例中,从数据源获取数据中,应该一次性创建一个字节切片作为其缓冲区,而不是每次从数据源中读取数据都进行一次内存分配,减少不必要的内存分配。

指针-优化垃圾回收

垃圾是什么?垃圾回收是什么?如何优化垃圾回收?

在Go语言中,垃圾是不在有指针指向的数据。垃圾回收自动检测未使用的内存,并将其回收。

栈是什么?栈的工作原理是什么?栈上储存内容的条件?

在Go语言中,栈是一个连续的内存块,执行线程中的每个函数调用都共享一个栈。

在栈上储存内容,必须在编译中知道多大。基础类型、数组、结构、指针类型

指针指向数据,必须是一个局部变量,且数据大小在编译是一致的,不能从函数中返回。

栈、堆储存什么数据?栈、堆数据什么时候释放?

在Go语言中,栈储存函数的参数和局部变量的值

堆储存。栈由编译器在函数退出后自动分配和销毁,堆一般由程序员手动释放。

在Go语言中,虽然变量申请在堆空间上,但是他有自动回收垃圾的功能,所以这些堆地址空间也无需手动回收,系统会在需要释放的时刻自动进行垃圾回收。

内存逃逸是什么情况出现的?

在Go语言中,如果指针变量被返回,当函数退出时,指针指向的数据不在有效,数据不能储存在栈中,指针指向的数据在栈上逃逸,然后把编译器将数据储存在堆中

栈数据逃到堆上


// 返回一个指向局部变量的指针
func createPerson(name string, age int) *Person {
	// 在函数内部创建一个 Person 对象,并返回其指针
	p := Person{Name: name, Age: age}
	return &p // 返回局部变量的指针,可能逃逸到堆上
}

func main() {
	// 调用 createPerson 函数,返回一个指向堆上数据的指针
	personPtr := createPerson("Alice", 25)

	// 在 main 函数中,personPtr 指向的数据可能已经逃逸到堆上
	fmt.Println(personPtr.Name, personPtr.Age)
}

// 定义一个 Person 结构体
type Person struct {
	Name string
	Age  int
}

分配到堆上

var p *int

func f() {
	var i int
	i = 1
	p = &i
	fmt.Println(p)
}
func main() {
	fmt.Println(p)
	f()
	fmt.Println(p)
}

分配到栈上

fun f(){
    p:=new(int)
    *p=1
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值