内容简介:相信看过Go源码的同学已经对看字面意思,接下来,我们就来了解一下Go的这个“不安全的指针”
相信看过 Go 源码的同学已经对 unsafe.Pointer
非常的眼熟,因为这个类型可以说在源码中是随处可见: map
、 channel
、 interface
、 slice
…但凡你能想到的内容,基本都会有 unsafe.Pointer
的影子。
看字面意思, unsafe.Pointer
是“不安全的指针”,指针就指针吧,还安不安全的是个什么鬼?
接下来,我们就来了解一下Go的这个“不安全的指针” unsafe.Pointer
。
什么叫变量
在了解指针之前,我们有必要先了解一下什么叫“变量”。
其实变量就是一个内存地址的名字,这听起来可能有些奇怪:指针不是地址码?
听我细细来讲:此地址非彼地址。通常,我们要在计算机内存中存数据,我们会怎么做?
我们肯定说:“计算机,在0x0201地址内存一个数100”。就这一句话,别看它糙,实际上在计算机中真就这么干的。然后我们接着说:“在0x0202中存什么,在0x0203中存什么,把0x0203中的值变为0x0201中的值…”
这些“0x0201”、“0x0202”、“0x0203”…这些数字儿是不是不太好记?写个代码是不是头都大了?
于是聪明的先人给想了个办法,把这些地址换成 代号 ,“0x0201”我叫x,“0x0202”我给他起个名字叫y,“0x0203”我给他起个名字叫z…
于是 “计算机,在0x0201地址内存一个数100”。就变成了 var x int =100
。
而这个这个代号就是变量。
0x0201地址 =============》 100 0x0201地址 ======》X ===》 100
果然,计算机界中的任何问题,都可以通过加一个中间层来解决。(#^.^#)
最后,计算机会在内存中存代号和变量地址的对应关系。
什么叫指针
我们印象中的指针这个概念,其实就是一个存了内存地址的对象,这个对象指向的内存地址可能是另外一个对象、函数或者结构体等。
这个理解没错,但是一定要理清楚指针和变量的关系。
在一般的指针中,由于指针只是一个地址,底层实现是一个unsigned int,所以在 C语言 中,指针之间的赋值和计算等同类型之间的操作很常见。
以下代码扫一眼,看看是否知道输出结果。
#include "stdio.h" int main(int argc, char const *argv[]) { char c = 'b'; int i = 1000; char *cp; int *ip; //指针的正常赋值 cp = &c; ip = &i; printf("cp[%p]\n", cp); //cp[0x7ffee904275f] printf("ip[%p]\n", ip); //ip[0x7ffee9042758] //指针的计算 cp = cp + 1; ip = ip + 1; printf("cp[%p]\n", cp); //cp[0x7ffee9042760] printf("ip[%p]\n", ip); //ip[0x7ffee904275c] //不同"类型"指针之间的赋值 cp = ip; printf("cp[%p] ip[%p]\n", cp, ip); //cp[0x7ffee904275c] ip[0x7ffee904275c] //不同指针之间的比较 输出true if (cp == ip) { printf("true\n"); } else { printf("false\n"); } }
通常意义上我们了解的不同类型的指针,可以归为“同一类型”,无论是int类型的指针还是char类型的指针,都称之为“指针类型”。
指针指向对象类型的约束对指针本身而言非常弱,因为在通常C语言中的定义不同类型的指针,只是为了调用的方便。例如一个指针指向了某一个结构体,那么我写代码的时候就可以方便的使用该结构体的属性字段;可以说通常意义上的C指针,是个“万能类型”的,啥类型的指针都和 void*
一样,万能!
所以,在C语言中,假如不使用指针,可以认为是机器在帮我们“打理”内存。
但是假如我们使用了指针,由于指针的自由度非常大,我们就可以自己“打理”内存了(PS:这里的打理仅限内存指向问题,分配和清除肯定必然不行)。
Go中常用的指针
在C语言中,指针的操作是完全不被约束的,这就非常的危险:程序猿在写的时候就得细心一点,拿着指针操作太多,指来指去,指到不该指的地方,就不好了~
所以Go语言在设计的时候,也考虑到了这个问题,就给现有的指针加了约束:把指针类型的数据当成了一种数据类型,在编译的时候做严格判断。
举个例子来说: *int
和 *string
是两种不同的类型,那既然类型都不同,那么 *int
的数据就不能够和 *string
类型的数据进行“互通”了,既不能相互赋值,也不能相互比较;
能不能加减运算呀?当然不能,因为“数字儿”是整型, *int
或者 *string
是其他类型而非整型。
Go语言就给指针套了个“类型”的帽子,一下子把指针限制的死死的了。
而且Go最后规定:指针类型还不能相互强制转换。
我们知道:int和string是可以相互转换的,既然指针归根到底是地址,也就是数字儿,那指针类型和int之间能否相互强制类型转换呢?答案是不行!
那 *int
和 *string
之间是否可以强制类型转换呢?答案是更不行,如果能强制转换了,前面说的给指针套的那顶“类型”的帽子,是不是就白做了?
unsafe.Pointer
好了,扯了那么多,终于到正题了。那么unsafe.Pointer指针是什么呢?
其实就一句话:就是C语言中那个牛逼到爆的什么都能指的不安全的指针。再确切一点是: void*
。
unsafe.Pointer
源码就两行:
type ArbitraryType int //表示任何类型 type Pointer *ArbitraryType //表示任何类型的指针
unsafe.Pointer
的源码注释还提供了关于 unsafe.Pointer
的四点重要的使用规则:
1、Go语言常规的任何类型的指针都可以转化为unsafe.Pointer类型 2、unsafe.Pointer类型可以转化为Go语言常规的任何类型的指针。 3、uintptr这个类型可以转化为unsafe.Pointer 4、unsafe.Pointer可以转化为uintptr
看完规则,你可能会问: uintptr
是啥?
来,没有比源码更好的解释的了:
注意看 uintptr
的位置,和 int
以及 uint
在一个包内,你可以认为 uintptr
与它们”同类”,只不过是指针的专属而已,但是你想自己定义用也能用。
对于 unsafe.Pointer
,多用于Go的编译时期;由于它可以绕过类型系统,直接去访问内存,所以它用起来效率会比较高,但是官方的态度是不太建议使用的,因为不太安全。我个人建议也是能不用就不用:毕竟为了这点儿效率带来的额外的附加成本比较高。
好了,我们最后总结一下Go的指针:
更多精彩内容,请关注我的微信公众号 互联网技术窝
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
国际大学生程序设计竞赛例题解
郭嵩山 / 电子工业出版社 / 2006-5 / 32.0
《国际大学生程序设计竞赛例题解1:数论、计算几何、搜索算法专集》可以作为高等院校有关专业的研究生和本科学生参加国际大学生程序设计竞赛的辅导教材,也可作为高等院校有关专业相关课程的教材和教学参考书,也比较适合作为中学青少年信息学奥林匹克竞赛省级及省级以上优秀选手备战信息学奥林匹克竞赛的培训教材及训练题集。一起来看看 《国际大学生程序设计竞赛例题解》 这本书的介绍吧!