Go指针与传递和new/make

一、什么是指针?

指针是一种特殊的变量,存储其它变量的**内存地址**.允许程序直接操作内存中的数据,而不是对数据的副本进行操作.
指针就是数据在内存当中的地址

**&取地址操作符**
 获取变量的地址

**解引用操作符**
 获取内存地址中对应变量的值

**Go语言当中,指针类型只有两种操作:&取址 和 *取值**

Go语言的值类型(int,float,bool,string,array,struct)都有对应的指针类型:如 *int *int64 *string

二、基本语法

var ptr *int 声明指针变量ptr,用于指向一个int类型变量的地址
&a 获取变量a的内存地址,返回一个指向该地址的指针
*ptr 读取ptr指针指向地址的值,这个操作被称作"解引用”
*ptr = 100 将100复制给ptr所指的变量

三、声明指针类型的变量

在Go中,可以使用指针引用任何类型的变量
var p *int //声明一个指向int类型的指针
var str *string //声明一个指向string类型的指针

/*
var i2 *int
fmt.Printf("类型是%v\n", *i2) //声明但未赋值的指针变量默认值为 nil,表示未指向任何内存地址,尝试访问 nil 指针指向的值会导致运行时错误
*/

示例:
// &取地址  *取值
x := 100
fmt.Printf("取地址:%v,类型是:%T\n", &x, x) //取地址:0xb041a8,类型是:int

//声明一个指向int类型的指针
var p *int
fmt.Printf("类型是:%T  声明的int类型指针值是:%v\n", p, &p) //类型是:*int  声明的int类型指针值是:0x1824060

//将地址值赋值给变量称为指针变量
p = &x
fmt.Printf("指针变量是:%v 类型是:%T 指针取值:%v\n", p, p, *p) //指针变量是:0xb041a8 类型是:*int 指针取值:100

四、初始化

Go 语言中,make 和 new 是两个用于内存分配的内置函数,但它们的使用场景和返回值有所不同

作用:      new 是一个分配内存的内置函数,它为类型 T 分配零值内存,并返回指向该内存的指针
返回值:    new(T) 返回的是 *T,即指向类型 T 的指针。
适用类型:  new 适用于所有类型,包括基本类型、结构体、数组等


作用:      make 也是一个分配内存的内置函数,但是它仅仅用于创建特定类型的对象,如 slice、map、channel
返回值:    make 返回的是一个特定类型的初始化后的值,是经过初始化之后的特定类型的引用。
适用类型:   make 仅适用于 slice、map 和 channel 类型

示例:
//使用 new 分配一个 int 类型,并返回指针
i := new(int)
fmt.Printf("类型是%T 指针地址是%v  指针取值%v\n", i, i, *i) //类型是*int 指针地址是0x1c0a0cc  指针取值0

//为返回指针赋值
*i = 10
fmt.Printf("值:%v\n", *i) //值:10


// 创建一个长度为 5 的 int 切片
s := make([]int, 5)
fmt.Printf("类型是%T 值是%v 切片长度是多少%v  切片容量是多少%v\n", s, s, len(s), cap(s)) //类型是[]int 值是[0 0 0 0 0] 切片长度是多少5  切片容量是多少5

//创建一个容量为 10 的切片
s1 := make([]int, 5, 10)
fmt.Printf("类型是%T 值是%v 切片长度是多少%v  切片容量是多少%v\n", s1, s1, len(s1), cap(s1)) //类型是[]int 值是[0 0 0 0 0] 切片长度是多少5  切片容量是多少10
s1 = append(s1, 11, 22, 33, 44, 55, 66)
fmt.Printf("类型是%T 值是%v 切片长度是多少%v  切片容量是多少%v\n", s1, s1, len(s1), cap(s1)) //切片长度是多少11  切片容量是多少20

//创建一个 map
m := make(map[string]int)
fmt.Printf("类型是%T 值是%v map长度是多少%v\n", m, m, len(m)) //类型是map[string]int 值是map[] 切片长度是多少0
m["LLj"] = 20
m["Liang"] = 30
fmt.Printf("类型是%T 值是%v map长度是多少%v\n", m, m, len(m)) //类型是map[string]int 值是map[LLj:20 Liang:30] map长度是多少2

五、传递指针给函数

指针可以作为参数传递给函数,从而可以在函数内部修改原始变量的值,而不是复制

func modify1(x *int) {
fmt.Printf("取值函数内:%v\n", *x) //取值函数内:0
//指针可以作为参数传递给函数,从而可以在函数内部修改原始变量的值,而不是复制
*x = 100
}

func modify2(y *int) {
fmt.Printf("取值函数内:%v \n", *y) //取值函数内20
//指针可以作为参数传递给函数,从而可以在函数内部修改原始变量的值,而不是复制
*y = 200
}

func main() {
//传递指针给函数
i1 := new(int)
fmt.Printf("类型是%T 取值:%v\n", i1, *i1) //类型是*int 取值是0
modify1(i1)
fmt.Printf("取值:%v\n", *i1) //取值:100

i3 := 20
modify2(&i3)
fmt.Printf("取值:%v\n", i3) //取值:200
}

六、指针的比较

使用!=与==比较指针引用的地址是否相同

//指针引用的地址是否相同
xx := 100
jj := &xx
fmt.Printf("xx的内存地址%v\n", &xx) //xx的内存地址0x1c0a10c
fmt.Printf("jj的内存地址%v\n", &jj) //jj的内存地址0x1c24068
fmt.Println(jj == &xx) //true
if jj == &xx {
    fmt.Println("等于")
} else {
    fmt.Println("不等于")
}

七、指针的比较

空指针和指针零值

**空指针**
  指针没有指向任何有效的内存地址,它将具有nil值,表示空指针.在使用指针之前,通常会检查指针是否为nil

**指针的零值**
  指针被声明但是没有被初始化,它会有零值nil

//声明指针类型变量,跟nil进行比较
var i *int
var s *string
fmt.Printf("值%v\n值%v\n", i, s) //值<nil> 值<nil>

if s == nil {
    fmt.Println("空") //空
} else {
    fmt.Println("不空")
}

八、传递大对象(传递结构体指针给函数)

在函数参数传递过程中,通过指针直接传递值本身,避免了复制整个对象,提高程序性能

type BigObject struct {
// 大对象的定义...
Name       string
Age        int
Occupation string
}

func processObject(obj *BigObject) {
  fmt.Println("对大对象进行处理...")
  obj.Name = "Llj"
  obj.Age = 20
  obj.Occupation = "IT"
}
func main(){
//得到定义的结构体
o := BigObject{}
fmt.Printf("BigObject定义前的值:%v\n", o) //BigObject定义前的值:{ 0 } 结构体中空字符串是默认值,空整形是0 所以这样显示
// 传递大对象的指针
processObject(&o)
fmt.Printf("被传入指针后BigObject的值:%v\n", o) //被传入指针后BigObject的值:{Llj 20 IT}
}

九、动态内存分配(函数返回结构体指针)

通过new函数可以在堆上分配内存,避免了在栈上分配固定大小的内存空间的限制.这在对于需要返回动态分配的数据或创建复杂数据结构时非常有用
type ComplexStruct struct {
// 复杂数据结构的定义...
Name       string
Age        int
Occupation string
}

func createComplexStruct() *ComplexStruct {
// 动态分配内存并返回指针
  n := new(ComplexStruct)
  fmt.Printf(" 初始化复杂数据结构内存...%v\n", n)  //初始化复杂数据结构内存...&{ 0 }
  fmt.Printf(" 初始化复杂数据结构取值...%v\n", *n) //初始化复杂数据结构取值...{ 0 }
  n.Name = "LLj"
  n.Age = 20
  n.Occupation = "IT"
  fmt.Printf(" 初始化复杂数据结构取值...%v\n", *n) //初始化复杂数据结构取值...{LLj 20 IT}

  return n
}
func main(){
// 对动态分配的数据结构进行操作...
c := createComplexStruct()
fmt.Printf("类型%T 值是%v\n", c, *c) //类型*main.ComplexStruct 值是{LLj 20 IT}
}

十、指针作为函数返回值

在函数中返回指针可以将函数内部创建的变量地址传递给调用者.这样可以避免复制整个变量,并允许调用者直接访问和修改函数内部的数据
 func createValue() *int {
     x := 10 // 在函数内部创建变量
     return &x // 返回变量的地址
 }

 func main() {
     p := createValue()
     fmt.Println(*p) // 输出通过指针访问的函数内部变量的值,即10
 }

posted @ 2025-06-18 17:40  梁博客  阅读(18)  评论(0)    收藏  举报