深入Go语言——内存
前言
关于内存部分的内容,是正在读的书中的第三部分,这一部分讲的是Go语言中的指针相关的知识,由于书中花费了一定篇幅去讲过于内存的问题,所以本周的总结报告命题为”内存“。
这并不是第三周全部学到的东西,只是开始,由于我现在还不能把学到的东西随手记录下来,所以选择先写一篇学习总结。我详细一篇应该是关于项目实战的。
学习思考
指针
提到指针,我们并不陌生,从开始学习程序设计就已经接触到了指针。对我来说,指针是一个既熟悉又陌生的东西。熟悉是因为在最开始的学习时,我和同学不止一次地讨论过指针也进行了一定的学习。陌生是因为我确实不是很喜欢学习C语言,以至于其真正的核心内容我学的都不扎实,学了就忘。
在Go语言中,由于变量分为值类型和引用类型,使得指针在函数传参的时候有了用处。对于值类型的变量而言,当函数的参数是指针类型时,函数对该参数的修改才会影响到该参数本身。
指针接收者
对于是否使用指针类型作为接收者,有以下几点参考:
- 如果接收者类型是 map、slice、channel 这类引用类型,不使用指针。
- 如果需要修改接收者,那么需要使用指针。
- 如果接收者是比较大的类型,可以考虑使用指针,因为内存拷员廉价,所以效率高。
什么情况下使用指针
从以上指针的详细分析中,我们可以总结出指针的两大好处:
- 可以修改所指向数据的值。
- 在变量复制、参数传值的时候可以节省内存。
不过.Go语言作为一种高级语言,在指针的使用上还是比较克制的。它在设计的时候就对指针进行了诸多限制,比如指针不支持运算,也不能取常量的指针。所以在思考是各使用时,我们也要保持克制的心态。
根据实战经验我总结了以下几点使用指针的建议,供你参考:
- 不要对map、slice、channel 这类引用类型使用指针。
- 如果需要修改方法接收者内部的数据或者状态时,需要使用指针。
- 如果需要修改参数的值或者内部数据时,也需要使用指针类型的参数。
- 如果是比较大的结构体,每次参数传递或者调用方法都要内存拷贝,内存占用多,这时候可以考虑使用指针。
- 像 int、bool 这样的小数据类型没必要使用指针。
- 如果需要并发安全,则尽可能地不要使用指针,使用指针一定要保证并发安全。
- 指针最好不要嵌套,也就是不要使用一个指向指针的指针,虽然Go语言允许这么做,但是这会使你的代码变得异常复杂。
思考题
指向接口的指针是否实现了该接口?为什么?
答:虽然指向具体类型的指针可以实现一个接口,但是指向接口的指针永远不可能实现该接口。
值类型?指针类型?引用类型?
初次看到这三个名词,我似懂非懂。值、指针、引用,为什么几个我认识的字放在一起,我就不太能分得清这些都是什么了。在上面的内容中有提到,Go语言中的变量分为两种,值类型和引用类型。那这个指针类型又是什么,和其他两种有什么关系呢。
值类型
值类型包括:整型、浮点型、字符串、布尔、数组和 struct 。
认真观察过这些类型,我们不难发现,当这些类型作为函数参数时,在函数内部对其进行修改并不会影响到其原来的值。这就是值类型的特点。原因如下:
观察上面的示意图不难得出,当 main
函数调用函数 modifyPerson
时,程序将变量 P 进行了一次复制,所以在函数 modifyPerson
试图修改变量 P 时,修改的是 P 的副本。值类型的变量在函数中均是如此。因此,在函数试图修改值类型的参数时,实际的参数并不会被修改。
指针类型
指针类型保存的值就是数据对应的内存地址,所以在函数传参采用值传递的原则下,拷贝的值也是内存地址。因此当函数想要对参数进行修改时,即使修改的是参数的副本,其内容仍是需要修改变量的地址。
引用类型
引用类型包括:切片、map、函数、chan和接口
对于引用类型,在参数传递时仍然是值传递,但其传递的值为指针,并不是其他编程语言中所谓的引用传递。
new和make
在声明一个变量时,常常会使用到的是 var
关键字和 :=
这两种方法。当我们尝试着去指针类型的变量时:
1 | func main() { |
当运行上面的代码时,编译器会报错。
这是因为指针类型的变量如果没有分配内存,其默认值为 nil
,因为它没有指向的内存,所以无法使用,强行使用就会出现 nil
指针的报错。
一个变量必须要经过声明、内存分配才能赋值,才能进行初始化。对于值类型,Go语言会在声明时就为它分配内存,以便后续的赋值操作。对于指针类型,Go语言并没有自动分配内存,所以不能对其进行赋值操作。
new函数
正是因为上面情况,new
函数派上了用场。
new
函数的作用就是根据传入的类型申请一块内存,然后返回指向这块内存的指针,指针指向的数据就是该类型的零值
make函数
关于make
函数,最常使用的场景就是 map、slice 和 chan 这三种类型的初始化。
make 函数就是上述三种类型的工厂函数,用于创建和初始化这几种类型。
make和new的区别
make
和 new
是两个⽤于分配内存的内建函数, 在使⽤场景和返回值类型上有明显的区别。
make
⽤于创建并初始化切⽚、映射和通道等引⽤类型。它返回的是被初始化的⾮零值(⾮nil)的引⽤类型。new
⽤于分配值类型的内存,并返回该值类型的指针。它返回的是分配的零值的指针。
总结:
new
只⽤于分配内存,返回⼀个指向地址的指针。它为每个新类型分配⼀⽚内存,初始化为0且返回类型*T的内存地址,它相当于&T{}make
只可⽤于 slice, map, channel 的初始化,返回的是引⽤
小结
总结一下,本周好像有一点懈怠了,做题和做项目的时候都有点心不在焉的。不过本周学到的东西还是不少的。
- 算法题已经完成了数组、双指针、滑动窗口和区间四个部分了。
- 项目目前完成了一半左右,希望下一周能全部搞完,再完成一篇博客。