Golang指针详解

要搞明白Go语言中的指针需要先知道3个概念:指针地址、指针类型和指针取值。

指针介绍

我们知道变量是用来存储数据的,变量的本质是给存储数据的内存地址起了一个好记的别名。比如我们定义了一个变量 a := 10 ,这个时候可以直接通过 a 这个变量来读取内存中保存的 10 这个值。
在计算机底层 a 这个变量其实对应了一个内存地址。
指针也是一个变量,但它是一种特殊的变量,它存储的数据不是一个普通的值,而是另
一个变量的内存地址。

Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。类型指针不能进行偏移和运算。

Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)*(根据地址取值)

指针地址和指针类型

每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。
Go 语言中使用&字符放在变量前面对变量进行取地址操作。
Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:*int、*int64、*string等。
取变量指针的语法如下:

 ptr := &v    // v的类型为T

 v:代表被取地址的变量,类型为T
    ptr:用于接收地址的变量,ptr的类型就为*T,称做T的指针类型。*代表指针。
Go 复制代码
package main

import "fmt"

func main() {

	//在计算机底层 a 这个变量其实对应了一个内存地址
	var a int = 1
	fmt.Printf("a的值为%v a的类型为%T a的内存地址为%v\n",a,a,&a)
	
	// 指针也是一个变量,但它是一种特殊的变量,它存储的数据不是一个普通的值,而是另一个变量的内存地址
	var b int = 10
	c := &b //c指针变量   c的类型 *int(指针类型)
	fmt.Printf("b的值为%v b的类型为%T b的内存地址为%v\n",b,b,&b)
	fmt.Printf("c的值 %v c的类型%T ", c, c)

	//每一个变量都有自己的内存地址
	var f int = 20
	d := &f
	fmt.Printf("f的值 %v f的类型%T f的地址%p\n", f, f, &f)
	fmt.Printf("d的值 %v d的类型%T d的地址%p", d, d, &d)
	



}

b := &a的图示:

指针取值

在对普通变量使用&操作符取地址后会获得这个变量的指针,然后可以对指针使用*操作,也就是指针取值,代码如下。

Go 复制代码
package main

import "fmt"

func main() {
	var e int = 30
	var d *int = &e
	// d := &e
	// *d :表示取出d这个变量对应的内存地址的值
	fmt.Println(d)
	fmt.Println(&e)
	fmt.Println(*d)  

	*d = 40			//改变d这个变量对应的内存地址的值(之前所赋的是e的内存地址的值)
	fmt.Println(e)
	

}


**总结:**取地址操作符&和取值操作符*是一对互补操作符,&取出地址,*根据地址取出地址
指向的值。

变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:

   1.对变量进行取地址(&)操作,可以获得这个变量的指针变量。
    2.指针变量的值是指针地址。
    3.对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。

指针传值示例

Go 复制代码
package main

import "fmt"

func fn1(x int) {
	x = 10
	// fmt.Println(x)
}

func fn2(x *int) {
	*x = 40
	// fmt.Println(*x)
}

func main() {
	var a = 5
	fn1(a)
	fmt.Println(a) //5;方法内的值并未改变a的内存地址,其原本的值所以没有变化
	fn2(&a)
	fmt.Println(a) //40 ;方法内传入的是指针类型,传入a改变了指针对应的内存地址,导致取指针的值变化了

}

new和make

在 Go 语言中 对于引用类型的变量,我们在使用的时候不仅要声明它,还要为它分配内存空间 ,否
则我们的值就没办法存储。(触发panic)
而对于值类型的声明不需要分配内存空间,是因为它们在声明的时候已
经默认分配好了内存空间。要分配内存,就引出来今天的 new 和 make。
Go 语言中 new 和 make 是内建的两个函数,主要用来分配内存。

make

make也是用于内存分配的,区别于new,它只用于slice、map以及chan的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。make函数的函数签名如下:

func make(t Type, size ...IntegerType) Type

make函数是无可替代的,我们在使用slice、map以及channel的时候,都需要使用make进行初始化,然后才可以对它们进行操作。

Go 复制代码
package main

import "fmt"

func main() {


	// var teseDemo map[string]string  错误
	// teseDemo["卫宫士郎"] = "sabar"
	// fmt.Println(teseDemo)

	var teseDemo = make(map[string]string)
	teseDemo["远坂应"] = "archer"
	fmt.Println(teseDemo)

	var demo []int
	demo = append(demo,1 ) // qppend是可以实现的,但是如果是直接demo[0] = 1
	fmt.Println(demo[0])

	// var demo1 []int
	// demo1[0] = 2 // 如果是直接demo[0] = 1,会因为没有分配内存而panic
	// fmt.Println(demo[0])

	var demo1 = make([]int, 4,4)
	demo1[1] = 12
	fmt.Print(demo1)

	//指针也是引用数据类型 不分配内存空间也会报错  错误
	// var a *int 
	// *a = 100
	// fmt.Println(*a)
}

new

new是一个内置的函数,它的函数签名如下:

    func new(Type) *Type

其中,

    1.Type表示类型,new函数只接受一个参数,这个参数是一个类型
    2.*Type表示类型指针,new函数返回一个指向该类型内存地址的指针。

new函数不太常用,使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值。举个例子:

Go 复制代码
	var a *int
	a = new(int)
	*a = 10
	fmt.Println(*a) //0

	var b = new(bool)
	fmt.Println(*b) //false
Go 复制代码
package main

import "fmt"

func main() {
		a := new(int)
		b := new(bool)
		fmt.Printf("%T\n", a) // *int
		fmt.Printf("%T\n", b) // *bool
		fmt.Println(*a)       // 0
		fmt.Println(*b)       // false
}

new与make的区别

   1.二者都是用来做内存分配的。
    2.make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;
    3.而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。
相关推荐
winks35 分钟前
Spring Task的使用
java·后端·spring
Null箘6 分钟前
从零创建一个 Django 项目
后端·python·django
云空10 分钟前
《解锁 Python 数据挖掘的奥秘》
开发语言·python·数据挖掘
秋意钟16 分钟前
Spring新版本
java·后端·spring
青莳吖20 分钟前
Java通过Map实现与SQL中的group by相同的逻辑
java·开发语言·sql
Buleall27 分钟前
期末考学C
java·开发语言
重生之绝世牛码29 分钟前
Java设计模式 —— 【结构型模式】外观模式详解
java·大数据·开发语言·设计模式·设计原则·外观模式
小蜗牛慢慢爬行36 分钟前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
Algorithm15761 小时前
云原生相关的 Go 语言工程师技术路线(含博客网址导航)
开发语言·云原生·golang
shinelord明1 小时前
【再谈设计模式】享元模式~对象共享的优化妙手
开发语言·数据结构·算法·设计模式·软件工程