golang 指针

栏目: C · 发布时间: 6年前

内容简介:1. uintptr类型:该类型实际上是一个数值类型,也是Go语言内建的数据类型之一。根据当前计算机架构的不同,它可以存储32位或64位的无符号整数,可以代表任何指针的位(bit)模式,也就是原始的内存地址。2.unsafe.Pointer:可以代表任何指向问:Go语言中哪些值是不可寻址的?

Go 语言中,有几种东西可以代表“指针”。

 1. uintptr类型:该类型实际上是一个数值类型,也是Go语言内建的数据类型之一。根据当前计算机架构的不同,它可以存储32位或64位的无符号整数,可以代表任何指针的位(bit)模式,也就是原始的内存地址。

 2.unsafe.Pointer:可以代表任何指向 可寻址的值 的指针,同时它也是前面提到的指针值和uintptr值之间的桥梁。也就是说,通过它,我们可以在这两种值之上进行双向的转换。

问:Go语言中哪些值是不可寻址的?

答:

  • 1.常量的值
const num = 123
//_ = &num // 常量不可寻址。
  • 2.基本类型值的字面量
//_ = &(123) // 基本类型值的字面量不可寻址。
  • 3.算术操作的结果值
//_ = &(123 + 456) // 算术操作的结果值不可寻址。
  • 4.对各种字面量的索引表达式和切片表达式的结果值。例外:对切片字面量的索引结果是可寻址的。
//_ = &([3]int{1, 2, 3}[0]) // 对数组字面量的索引结果值不可寻址。
//_ = &([3]int{1, 2, 3}[0:2]) // 对数组字面量的切片结果值不可寻址。
_ = &([]int{1, 2, 3}[0]) // 对切片字面量的索引结果值却是可寻址的。
//_ = &([]int{1, 2, 3}[0:2]) // 对切片字面量的切片结果值不可寻址。
//_ = &(map[int]string{1: "a"}[0]) // 对字典字面量的索引结果值不可寻址。
  • 5.对字符串变量的索引表达式和切片表达式的结果值。
var str = "abc"
_ = str
//_ = &(str[0]) // 对字符串变量的索引结果值不可寻址。
//_ = &(str[0:2]) // 对字符串变量的切片结果值不可寻址。
str2 := str[0]
_ = &str2 // 但这样的寻址就是合法的。
  • 6.对字典变量的索引表达式的结果值
var map1 = map[int]string{1: "a", 2: "b", 3: "c"}
_ = map1
//_ = &(map1[2]) // 对字典变量的索引结果值不可寻址。
  • 7.函数字面量和方法字面量,以及对它们的调用表达式的结果值。
//_ = &(fmt.Sprintf) // 标识符代表的函数不可寻址。
//_ = &(fmt.Sprintln("abc")) // 对函数的调用结果值不可寻址。

dog := Dog{"little pig"}
_ = dog
//_ = &(dog.Name) // 标识符代表的函数不可寻址。
//_ = &(dog.Name()) // 对方法的调用结果值不可寻址。
  • 8.结构体字面量的字段值,也就是对结构体字面量
//_ = &(Dog{"little pig"}.name) // 结构体字面量的字段不可寻址。
  • 9.类型转换表达式的结果值
//_ = &(interface{}(dog)) // 类型转换表达式的结果值不可寻址。
  • 10.类型断言表达式的结果值
dogI := interface{}(dog)
_ = dogI
//_ = &(dogI.(Named)) // 类型断言表达式的结果值不可寻址。
named := dogI.(Named)
_ = named
//_ = &(named.(Dog)) // 类型断言表达式的结果值不可寻址。
  • 11.接受表达式的结果值
var chan1 = make(chan int, 1)
chan1 <- 1
//_ = &(<-chan1) // 接收表达式的结果值不可寻址。

原因:

1. 不可变 的值不可寻址。常量、基本类型的值字面量、字符串变量的值、函数以及方法的字面量都是如此。这样规定有安全方面的考虑。

2.绝大多数被视为 临时结果 的值都是不可寻址的。算术操作的结果值属于临时结果,针对值字面量的表达式结果值也属于临时结果。但有一个例外,对切片字面量的索引结果值虽然也属于临时结果,但却是可寻址的。

3.若拿到的某值的指针可能会破坏程序的一致性,那么就是 不安全的 ,该值就不可寻址。由于字典的内部机制,对字典的索引结果值的取值操作都是不安全的。另外,获取由字面量或标识符代表的函数或方法的地址显然也是不安全的。

最后,如果我们把临时结果赋给一个变量,那么它就是可寻址的了。如此一来,取得的指针向就是这个变量持有的那个值了。

问:不可寻址的值在使用上有哪些限制?

答:

 无法使用取址操作符&获取它们的指针,会使编译器报错。

demo:

func New(name string) Dog {
      return Dog(name)
}
New("litter pig").SetName("monster")

调用表达式dog.SetName("monster")会被自动地转译成(&dog).SetName("monster")

由于New函数的调用结果是不可寻址的,所以无法进行取址操作。因此,编译器会报告两个错误,1:不能在New("litter pig")的结果值上调用指针方法 ; 2:不能取得New("litter pig")的地址。

问:怎样通过unsafe.Pointer操纵可寻址的值?

答:

demo:

dog := Dog{"litter pig"}
dogP := &dog
dogPtr := uintPtr(unsafe.Pointer(dogP))

 先声明了一个Dog类型的变量dog,然后用取址操作符&,取出了它的指针值,并把它赋给了变量dogP.

 最后,使用了两个类型转换,先把dogP转换成一个unsafe.Pointer类型的值,然后紧接着又把后者转换成了一个uintptr的值,并把它赋给了变量dogPtr。这背后隐藏着一些转换规则:

 1.一个指针值(比如*Dog类型的值)可以被转换为一个unsafe.Pointer类型的值,反之亦然。

 2.一个uintptr类型的值也可以被转换为一个unsafe.Pointer类型的值,反之亦然。

 3.一个指针值无法被直接转换成一个uintptr类型的值,反过来也是如此。

namePtr := dogPtr + unsafe.Offestof(dog.name)
nameP := (*string)(unsafe.Pointer(namePtr))

unsafe.Offestof函数用于获取两个值在内存中的起始存储地址之间的偏移量,以字节为单位。

通过偏移量跟结构体在内存中的起始存储地址(dogPtr ),把它们相加我们就可以得到dogP的name字段值的起始存储地址(namePtr );

再通过两次类型转换把namePtr 的值转换成一个*string的类型的值,就得到了指向dogP的name字段的指针值。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

并行计算导论

并行计算导论

Ananth Grama、George Karypis、张武、毛国勇、Anshul Gupta、Vipin Kumar、程海英 / 张武、毛国勇、程海英 / 机械工业出版社 / 2005-1-1 / 49.00元

《并行计算导论》(原书第2版)全面介绍并行计算的各个方面,包括体系结构、编程范例、算法与应用和标准等,涉及并行计算的新技术,也覆盖了较传统的算法,如排序、搜索、图和动态编程等。《并行计算导论》(原书第2版)尽可能采用与底层平台无关的体系结构并且针对抽象模型来设计处落地。书中选择MPI、POSIX线程和OpenMP作为编程模型,并在不同例子中反映了并行计算的不断变化的应用组合。一起来看看 《并行计算导论》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具