计算机中所有的数据都必须放在内存中,不同类型的数据占用的字节数不一样,例如 int 占用 4 个字节。为了正确地访问这些数据,必须为每个字节都编上号码,就像门牌号、身份证号一样,每个字节的编号是唯一的,根据编号可以准确地找到某个字节。
我们将内存中字节的编号称为地址(Address)或指针(Pointer)。地址从 0 开始依次增加,对于 32 位环境,程序能够使用的内存为 4GB,最小的地址为 0,最大的地址为 0XFFFFFFFF。
数据在内存中的地址也称为指针,如果一个变量存储了一份数据的指针,我们就称它为指针变量。
Go语言中使用对于指针存在两种操作: 取址和取值。
var x = 100
// 取址符:& 取值符:*
fmt.Println("x的地址:", &x)
// 将地址值赋值给的变量称为指针变量
var p *int
p = &x
fmt.Println("p的值:", p)
fmt.Println("p地址对应的值", *p)
关于地址的格式化打印
var x = 10
fmt.Printf("%p\n", &x)
x = 100
fmt.Printf("%p\n", &x)
fmt.Println(*&x)
关于指针的应用:
// 当使用等号将一个变量的值赋给另一个变量时,如 x = y ,实际上是在内存中将 i 的值进行了拷贝
var x = 10
var y = x
var z = &x
x = 20
fmt.Println(y)
fmt.Println(*z)
*z = 30
fmt.Println(x)
练习
var x = 10
var y = &x
var z = *y
x = 20
fmt.Println(x)
fmt.Println(*y)
fmt.Println(z)
//=====================================
var a = 100
var b = &a
var c = &b
**c = 200
fmt.Println(a)
Go语言的指针类型变量即拥有指针高效访问的特点,又不会发生指针偏移和运算,从而避免了非法修改关键性数据的问题。
new函数
new 和 make 是 Go 语言中用于内存分配的原语。简单来说,new 只分配内存,make 用于初始化 slice、map 和 channel。
之前我们学习的基本数据类型声明之后是有一个默认零值的,但是指针类型呢?
在Go语言中,new
函数和make
函数都用于创建新的对象,但是它们有不同的用途和适用范围。
new
函数:new
函数用于创建值类型的对象,并返回一个指向该对象的指针。它的语法是new(T)
,其中T
表示类型。new
函数会分配内存,并将分配的内存初始化为零值,然后返回指向该内存的指针。这个指针可以直接访问和操作对象的字段。例如:
p := new(int) // 创建一个新的int类型对象,并返回指向该对象的指针
fmt.Println(*p) // 输出0,因为内存被初始化为零值
*p = 42 // 修改对象的值
fmt.Println(*p) // 输出42
make
函数:make
函数用于创建引用类型的对象,如切片、映射和通道,并返回一个已初始化的对象。它的语法是make(T, args)
,其中T
表示类型,args
表示用于初始化的参数。make
函数会分配内存,并初始化对象的相关字段,返回一个可以直接使用的对象。例如:
s := make([]int, 5) // 创建一个长度为5的切片,并初始化为零值
fmt.Println(s) // 输出[0 0 0 0 0]
s[0] = 1 // 修改切片的值
fmt.Println(s) // 输出[1 0 0 0 0]
总结:
new
函数用于创建值类型的对象,并返回一个指向该对象的指针。make
函数用于创建引用类型的对象,并返回一个已初始化的对象。new
函数只分配内存,不进行初始化,返回的是指向零值的指针。make
函数分配内存并进行初始化,返回的是已初始化的对象。
需要根据具体的需求和类型选择使用new
函数还是make
函数。
var p *int
// fmt.Println(p) // <nil>
// fmt.Println(*p) // 报错,并没有开辟空间地址
*p = 10. // 报错
我们可以看到初始化⼀个指针变量,其值为nil,nil的值是不能直接赋值的。通过内建的new函数返回⼀个指向新分配的类型为int的指针,指针值为0xc00004c088,这个指针指向的内容的值为零(zero value)。
var p *int = new(int)
fmt.Println(p) // 0x14000122008
fmt.Println(*p) // 0
*p = 10
fmt.Println(*p) // 10
make返回的还是引⽤类型本⾝;⽽new返回的是指向类型的指针。后面再详细介绍
func 第1个_指针1() {
//&变量,获取变量地址
var x = 10
fmt.Printf("赋值之前x的对应地址:%p\n", &x)
x = 100
fmt.Printf("赋值之后x的对应地址:%p\n", &x)
fmt.Println(x)
}
func 第2个_指针2() {
//取址
//赋值
//取值
//(1) 或取地址:&变量
var x = 10 //x称为整型变量
//(2) 地址赋值:指针类型
var p *int //p是一个整型指针类型
p = &x // var p = &x
fmt.Println(p)
//(3)取值操作,*指针变量
fmt.Println(*p, reflect.TypeOf(*p))
}
func 第3个_接收指针类型() {
//指针类型存储地址值
//地址本身也有一个类型
var x = 10
fmt.Println(&x)
//p就是指针变量
var p *int
p = &x
fmt.Println(p)
fmt.Println(*p, reflect.TypeOf(*p))
*p = 100
fmt.Println(*p)
fmt.Println(x)
}
func 第4个_思考题() {
var a = 1
var b = a
b = 100
fmt.Println(a, b)
//如何指向同一块儿空间
}
func 第5个_指针案例3() {
var a = 100
fmt.Println(reflect.TypeOf(a))
var b = &a
fmt.Println(reflect.TypeOf(b))
var c = &b
fmt.Println(reflect.TypeOf(c))
**c = 200
fmt.Println(a)
}
func 第6个_指针案例4() {
p1 := 1 //p1类型
p2 := &p1 //*int类型
*p2++
fmt.Println(p1)
fmt.Println(*p2)
}
func 第7个_new函数1() {
//基本数据类型(整型浮点型,字符串,布尔,数组,结构体,类型)属于值类型
//值类型的特定:当声明未赋值之前存在一个默认值(zero value)
var x int
var name string
fmt.Println(x, name)
//指针类型属于引用类型,包括切片,map,channel都属于引用类型
//引用类型当声明未赋值之前是没有开辟空间的,即没有默认值
var p *int
p = new(int)
*p = 10
fmt.Println(*p)
}