golang指针相关

news/2024/11/28 11:48:36/

指针相关的部分实在是没有搞太明白,抽时间来总结下。

1.指针相关基础知识
比如现在有一句话:『谜底666』,这句话在程序中一启动,就要加载到内存中,假如内存地址0x123456,然后我们可以将这句话复制给变量A,这句话的地址复制给变量B,首先变量B就是一个指针变量。
& 运算符:用于取地址
*运算符:用于根据地址取值
PS:地址是干嘛的:每个变量在运行是都有一个地址,这个地址代表该变量在内存中的位置

func main() {a := 10b := &a //取地址  等同于 var b *int = &a  此时的*int 代表变量b是一个*int类型fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*intfmt.Println(&b)                    // 0xc00000e018 取b的地址(b本身也是一个指针类型)
}

放一张图:
在这里插入图片描述
对普通变量使用&操作符取地址的话,会得到这个变量的指针,然后可以用*操作符进行指针取值。

func main() {//指针取值a := 10b := &a // 取变量a的地址,将指针保存到b中fmt.Printf("type of b:%T\n", b)c := *b // 指针取值(根据指针去内存取值)fmt.Printf("type of c:%T\n", c)fmt.Printf("value of c:%v\n", c)
}
输出如下:
type of b:*int
type of c:int
value of c:10

再来个例子:

package mainimport "fmt"func main() {var (a int = 100b int = 300)fmt.Printf("交换前a的值: %d\n", a)fmt.Printf("交换前b的值: %d\n\n", b)swap(&a, &b)fmt.Printf("交换后a的值: %d\n", a)fmt.Printf("交换后b的值: %d\n", b)
}func swap(x, y *int) {//值传递,两数交换*x, *y = *y, *x
}
//输出结果如下
交换前a的值: 100
交换前b的值: 300交换后a的值: 300
交换后b的值: 100

小结:

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

2.如何进行指针传值

func modify1(x int) {x = 100
}
func modify2(x *int) {   //表示x是一个*int类型*x = 100   //对变量x取值后,赋值100,由于在主函数传的是变量a的地址,所以相当于对a赋值100
}
func main() {a := 10modify1(a)fmt.Println(a) // 10  不改变,modify2(&a)fmt.Println(a) // 100
}

上述代码也可以这样操作:

func modify3(x int) int{x = 100return x
}func main() {a := 10a = modify3(a)  // 这里虽然都叫a,但是内存地址是不一样的fmt.Println(a) // 100
}

再来一个栗子:

func main() {a := 10pointerDemo03(&a)fmt.Println(a)
}// 注意,指针作为函数的时候,参数也要加上*
func pointerDemo03(a *int) {*a = 20
}
//到现在位置应该明白了,不用再赘述这个了

3.new(不太在实际项目中见到)
使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值,每次调用 new 函数都会返回唯一的地址变量(当定义一个空 struct 时,通过 new 创建一个变量时,返回的地址是相同的。):

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

关于new函数再举一个栗子:

func main() {// 创建一个未命名的 int 类型变量,初始值是 0,返回值 p 是指向 int 类型变量的指针。p := new(int)fmt.Println(p, *p) // 0xc00001c0b8 0// 创建一个未命名的 string 类型变量,初始值是 "", 返回值是 q 是指向 string 类型变量的指针。q := new(string)fmt.Println(q, *q) // 0xc000010240 ""*q = "a"fmt.Println(*q) // a
}

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

4.数组指针与指针数组
指针数组是指元素为指针类型的数组,数组指针是获取数组变量的地址
数组指针:

func pointerDemo05() {arr := [10]int{1, 2, 3, 3, 4, 5}var p *[10]int  //定义p是一个数组指针p = &arr        //对这个数组指针赋值,赋值的是变量arr的地址fmt.Println(*p)      // 获取数组中的全部数据fmt.Println((*p)[0]) // 获取指定数组中索引的数据,因为*p是先运算,所以要先加括号,否则编译报错fmt.Println(p[0])    // 获取指定数组中索引的数据,这个格式和加括号一样,但是简化的写法for i := 0; i < len(p); i++ {fmt.Print(p[i], ",")}
}

指针数组:元素为指针类型的数组

func pointerDemo07() {var p [2]*int //注意和上面的区别a := 10b := 20// 变量a的内存地址保存在指针数组p的0索引,b保存在1索引p[0] = &ap[1] = &bfmt.Println(p)            // 获取p数组中的内存地址fmt.Println(*p[0], *p[1]) // 获取p数组中的指定索引数据for i := 0; i < len(p); i++ {fmt.Println(*p[i]) // 获取p数组中的所有的数据}for key, value := range p {fmt.Println(key, *value)}
}

5.结构体指针
直接使用结构体的方式(用new):

type People struct{Name stringAge int
}peo := new(People)
//因为结构体本质是值类型,所以创建结构体指针时已经开辟了内存空间
fmt.Println(peo == nil) //输出:false
//由于结构体中属性并不是指针类型,所以可以直接调用
peo.Name = "jeff"
fmt.Println(peo)//输出:&{jeff 0}
peo1:=peo
peo1.Name="高级语言"
fmt.Println(peo1,peo)//输出:&{高级语言 0} &{高级语言 0}

也可以声明结构体指针来进行赋值:

//声明结构体指针
var peo *People   //表示变量peo是一个*People类型(结构体指针类型)
//给结构体指针赋值
peo = &People{"jeff", 18}
/*
上面代码使用短变量方式如下
peo:= &People{"jeff", 18}*/
fmt.Println(peo)

结构体指针比较的是地址,*结构体指针取出地址中对应的值

p1 := People{"jeff", 18}
p2 := People{"jeff", 18}
fmt.Printf("%p %p\n", &p1, &p2) //输出地址不同
fmt.Println(p1 == p2)           //输出:truep3 := new(People)
p3 = &People{"jeff", 18}
//结构体变量不能和指针比较,使用*指针取出地址中值
fmt.Println(p1 == *p3) //输出:truep4 := &People{"jeff", 18}
//指针比较的是地址
fmt.Println(p3 == p4) //输出:false

PS:使用new来初始化结构体后,得到的是结构体的地址,栗子如上
再来一个栗子:

type Student struct {// 成员名称不加var关键字id   intname stringage  intaddr string
}
func main() {stu := Student{001, "itzhuzhu", 23, "广州"}var p *Studentp = &stupointerDemo10(p)fmt.Println(stu)
}func pointerDemo10(p *Student) {p.addr = "深圳"
}
//输出结果为{1 itzhuzhu 23 深圳}

下面这个同上面:

func main() {stu := Student{001, "itzhuzhu", 23, "广州"}//var p *Student//p = &stupointerDemo10(&stu)fmt.Println(stu)
}func pointerDemo10(p *Student) {p.addr = "深圳"
}

http://www.ppmy.cn/news/41785.html

相关文章

CentOS Ubuntu Debian三个linux的异同对比

Linux有非常多的发行版本&#xff0c;从性质上划分&#xff0c;大体分为由商业公司维护的商业版本与由开源社区维护的免费发行版本。 商业版本以Redhat为代表&#xff0c;开源社区版本则以debian为代表。这些版本各有不同的特点&#xff0c;在不同的应用领域发挥着不同的作用&…

执行命令行程序测试自动化

这几天有一个小工具需要做测试&#xff0c;是一个命令行工具&#xff0c;这个命令行工具有点类似mdbg等命令行工具&#xff0c;即程序运行后&#xff0c;在命令行等待用户敲入的命令&#xff0c;处理命令并显示结果&#xff0c;再继续等待用户敲入新的命令。 原来的测试用例都…

ROS开发之如何制作launch启动文件?

文章目录0、引言1、Launch文件语法2、Launch示例0、引言 笔者因研究课题涉及ROS开发&#xff0c;学习了古月居出品的ROS入门21讲&#xff0c;为巩固launch的知识&#xff0c;本文将ROS的launch启动文件制作一讲内容进行总结。launch文件通过XML文件实现多节点的配置和启动&…

那些警示良言——鲁迅

鲁迅的警示言 1、中国人的性情是总喜欢调和折中的&#xff0c;譬如你说&#xff0c;这屋子太暗&#xff0c;须在这里开一个窗&#xff0c;大家一定不允许的。但如果你主张拆掉屋顶他们就来调和&#xff0c;愿意开窗了。 ——《无声的中国》一九二七年 2、我每看运动会时&…

如何在windows/linux下启动OpenOffice

上面一篇文章使用openOffice来实现预览word、excel、pdf、txt等的功能时&#xff0c;发现openOffice没有启动&#xff0c;也怕有些同学安装后不会启动&#xff0c;所以便写下这一篇文章&#xff0c;来为大家说明如何启动openOffice&#xff0c;上一篇讲的如何下载安装openOffic…

2023年咸阳市《网络搭建与应用》专业技能大赛试题

竞赛说明 竞赛内容发布“网络搭建与应用”赛项竞赛共分三个部分,其中: 第一部分:网络搭建及安全部署项目(500分) 第二部分:服务器配置及应用项目(480分) 第三部分:职业规范与素养(20分) 竞赛注意事项禁止携带和使用移动存储设备、计算器、通信工具及参考资料。请根据…

【Linux】一篇文章, 掌握Linux进程信号

文章目录进程信号介绍进程信号进程信号的处理signal() 捕捉信号用户层产生进程信号的方式键盘产生进程信号系统调用产生进程信号kill()raise()abort()软件条件产生进程信号硬件异常产生进程信号除0 和 越界访问、野指针 如何产生相应信号除0越界访问、野指针core dumpcore 文件…

【Nginx】 如何在已经安装好的Nginx上增加新模块

前言 需要对NGINX 进行模块扩展&#xff0c;如果已经安装好了Nginx有不想重新安装覆盖的前提下如何新增模块呢? 下面通过安装nginx-http-flv-module作为示例进行说明&#xff0c;安装其他模块也是同样的道理。 下载第三方模块源码 https://github.com/winshining/nginx-ht…