GO语言内存操作指导—unsafe的使用
在unsafe包里面,官方的说明是:A uintptr is an integer, not a reference.Converting a Pointer to a uintptr creates an integer value with no pointer semantics. Even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed.
实际上Uintptr是一个int类型的变量,不是一个引用,没有pointer的语义
INVALID: uintptr cannot be stored in variable before conversion back to Pointer.
|
1
|
u := uintptr(p) |
|
2
|
p = unsafe.Pointer(u + offset) |
1 官方只建议在如下几种情况下可以使用uintptr
The remaining patterns enumerate the only valid conversions from uintptr to Pointer.
1.1 If p points into an allocated object, it can be advanced through the object by conversion to uintptr, addition of an offset, and conversion back to Pointer.
就是在需要用到地址偏移的操作的时候,将unsafe.pointer转换成uintptr,添加偏移之后,再转回来。
|
1
|
p = unsafe.Pointer(uintptr(p) + offset) |
|
2
|
|
|
3
|
// The most common use of this pattern is to access fields in a struct |
|
4
|
// or elements of an array: |
|
5
|
// |
|
6
|
// // equivalent to f := unsafe.Pointer(&s.f) |
|
7
|
f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f)) |
|
8
|
// |
|
9
|
// // equivalent to e := unsafe.Pointer(&x[i]) |
|
10
|
e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0])) |
1.2 Conversion of a Pointer to a uintptr when calling syscall.Syscall
这种我们不涉及
1.3 Conversion of the result of reflect.Value.Pointer or reflect.Value.UnsafeAddr from uintptr to Pointer.
包里面对这一块说明的很清楚:
Package reflect’s Value methods named Pointer and UnsafeAddr return type uintptr instead of unsafe.Pointer to keep callers from changing the result to an arbitrary type without first importing "unsafe". However, this means that the result is fragile and must be converted to Pointer immediately after making the call
由于这种方法返回的不是unsafe.pointer, 因此需要立马转回unsafe.pointer,再需要做这样的操作的时候。
|
1
|
p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer())) |
|
2
|
|
|
3
|
// INVALID: uintptr cannot be stored in variable |
|
4
|
// before conversion back to Pointer. |
|
5
|
u := reflect.ValueOf(new(int)).Pointer() |
|
6
|
p := (*int)(unsafe.Pointer(u)) |
1.4 Conversion of a reflect.SliceHeader or reflect.StringHeader Data field to or from Pointer.
As in the previous case, the reflect data structures SliceHeader and StringHeader declare the field Data as a uintptr to keep callers from changing the result to an arbitrary type without first importing “unsafe”. However, this means that SliceHeader and StringHeader are only valid when interpreting the content of an actual slice or string value.
这个用法只在想要获取slice或者string这两个数据类型结构体的各个字段的值的时候是有效的。并不能做其它的使用。
|
1
|
// In this usage hdr.Data is really an alternate way to refer to the underlying |
|
2
|
// pointer in the string header, not a uintptr variable itself. |
|
3
|
var s string |
|
4
|
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1 |
|
5
|
hdr.Data = uintptr(unsafe.Pointer(p)) // case 6 (this case) |
|
6
|
hdr.Len = n |
|
7
|
// |
|
8
|
// INVALID: a directly-declared header will not hold Data as a reference. |
|
9
|
var hdr reflect.StringHeader |
|
10
|
hdr.Data = uintptr(unsafe.Pointer(p)) |
|
11
|
dr.Len = n |
|
12
|
s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost |
这种方法虽然官方建议,但本身就是不安全的。不建议使用,在go语言之后的版本这种用法还会被替换掉
|
1
|
// StringHeader is the runtime representation of a string. |
|
2
|
// It cannot be used safely or portably and its representation may |
|
3
|
// change in a later release. |
|
4
|
// Moreover, the Data field is not sufficient to guarantee the data |
|
5
|
// it references will not be garbage collected, so programs must keep |
|
6
|
// a separate, correctly typed pointer to the underlying data. |
|
7
|
type StringHeader struct { |
|
8
|
Data uintptr |
|
9
|
Len int |
|
10
|
} |
|
11
|
|
|
12
|
// stringHeader is a safe version of StringHeader used within this package. |
|
13
|
type stringHeader struct { |
|
14
|
Data unsafe.Pointer |
|
15
|
Len int |
|
16
|
} |
2 现在我们的使用方法达成的一个共识是uintptr不能作为临时变量保存
我们在需要操作地址偏移的时候,再将unsafe.pointer转换成uintptr。其它指针的传递只能都使用unafe.pointer.
就拿取切片的首地址的操作来说:
2.1先获取切片底层数组的首地址的unsafe.pointer

2.2将unsafe.pointer作为值转递

2.3在使用uintptr做地址偏移的时候,千万不要保存成局部变量

GO语言内存操作指导—unsafe的使用的更多相关文章
- C语言嵌入式系统编程修炼之三:内存操作
数据指针 在嵌入式系统的编程中,常常要求在特定的内存单元读写内容,汇编有对应的MOV指令,而除C/C++以外的其它编程语言基本没有直接访问绝对地址的能力.在嵌入式系统的实际调试中,多借助C语言指针所具 ...
- 【C/C++】C语言嵌入式编程修炼·背景篇·软件架构篇·内存操作篇
C 语言嵌入式系统编程修炼之一:背景篇 不同于一般形式的软件编程,嵌入式系统编程建立在特定的硬件平台上,势必要求其编程语言具备较强的硬件直接操作能力.无疑,汇编语言具备这样的特质.但是,归因于汇编语言 ...
- C# unsafe模式内存操作深入探索
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Run ...
- C语言学习笔记--内存操作常见错误
1. 野指针 (1)指针变量中的值是非法的内存地址,进而形成野指针 (2)野指针不是 NULL 指针,是指向不可用内存地址的指针 (3)NULL 指针并无危害,很好判断,也很好调试 (4)C 语言中无 ...
- C语言处理字符串及内存操作
字符串处理函数 1.字符串长度 strlen表示包含的字符的个数,size_t strlen(char cosnt *string), 返回的是size_t类型,它是无符号整数类型,在表达式中进行运算 ...
- C语言字符串操作总结大全(超详细)
本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下 1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat( ...
- C语言字符串操作常用库函数
C语言字符串操作常用库函数 *********************************************************************************** 函数 ...
- iOS学习08之C语言内存管理
本次主要学习和理解C语言中的内存管理 1.存储区划分 按照地址从高到低的顺序:栈区,堆区,静态区,常量区,代码区 1> 栈区:局部变量的存储区域 局部变量基本都在函数.循环.分支中定义 栈区的内 ...
- 【转】《深入理解计算机系统》C程序中常见的内存操作有关的典型编程错误
原文地址:http://blog.csdn.net/slvher/article/details/9150597 对C/C++程序员来说,内存管理是个不小的挑战,绝对值得慎之又慎,否则让由上万行代码构 ...
- C 语言文件操作
C 语言文件操作 1. 数据流: 程序与数据的交互以流的形式进行.fopen 即打开数据流,fclose 即刷新数据流. 所谓数据流,是一种抽象,表示这段数据像流一样,需要逐步接收,不 ...
随机推荐
- HiveSql调优系列之Hive严格模式,如何合理使用Hive严格模式
目录 综述 1.严格模式 1.1 参数设置 1.2 查看参数 1.3 严格模式限制内容及对应参数设置 2.实际操作 2.1 分区表查询时必须指定分区 2.2 order by必须指定limit 2.3 ...
- C语言怎么给函数添加形参的默认值
以下内容为本人的著作,如需要转载,请声明原文链接微信公众号「englyf」https://www.cnblogs.com/englyf/p/16637890.html 如果不是机缘巧合,当年转到C++ ...
- Elasticsearch7.6.2 RestHighLevelClient查询用法 must should(and or 关系)
1. 引入jar <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId&g ...
- Jsoup爬取网上数据完成翻译
Jsoup使用 首先进入Jsoup下载jar包 然后打开IDEA创建一个普通的java项目 在项目结构里创建 lib 目录 但是我们这样并不能直接进行使用 需要添加路径 右键点击 然后添加路径 选择模 ...
- Linux安装RabbitMQ教程(文件下载地址+安装命令+ 端口开放 + 用户创建 +配置文件模板+端口修改)
前言 1.安装RabbitMQ前需先安装erlang, 且两者需要版本对应, 否则无法正常启动RabbitMQ (本教程使用22.0.7版本的erlang和3.8.6版本的Rabbitmq) 版本对应 ...
- [Python]-sklearn模块-机器学习Python入门《Python机器学习手册》-02-加载数据:加载数据集
<Python机器学习手册--从数据预处理到深度学习> 这本书类似于工具书或者字典,对于python具体代码的调用和使用场景写的很清楚,感觉虽然是工具书,但是对照着做一遍应该可以对机器学习 ...
- 并发原理 — CPU原子性指令(一)
本篇文章将以Intel CPU作为讨论基础 一.并发的由来 一台计算机有2个cpu,其中CPU1执行程序A,CPU2执行程序B,由于程序A和程序B是两个不同的应用程序,所以它们两个之间并不存在并发问题 ...
- ProxySQL Cluster 概述
文章转载自:https://blog.csdn.net/n88Lpo/article/details/79608639 前言 在ProxySQL 1.4.2 之前,ProxySQL 单点的解决方法有配 ...
- CentOS7配置nodejs环境
# 安装 wget https://nodejs.org/dist/v12.18.3/node-v12.18.3-linux-x64.tar.xz tar xf node-v12.18.3-linux ...
- Elasticsearch:如何调试集群状态 - 定位错误信息
文章转载自:https://blog.csdn.net/UbuntuTouch/article/details/108973356