uintptr 和 unsafe普及

uintptr

在Go的源码中uintptr的定义如下:

/* uintptr is an integer type that is large enough to hold the bit pattern of any pointer.
从英文注释可以看出 uintptr是一个整形,它的大小能够容纳任何指针的位模式,它是无符号的,最大值为:18446744073709551615,怎么来的,int64最大值 * 2 +1
*/
type uintptr uintptr

位模式:内存由字节组成.每个字节由8位bit组成,每个bit状态只能是0或1.所谓位模式,就是变量所占用内存的所有bit的状态的序列

指针大小:一个指针的大小是多少呢?在32位操作系统上,指针大小是4个字节,在64位操作系统上,指针的大小是8字节,

所以uintptr能够容纳任何指针的位模式,总的说uintptr表示的指针地址的值,可以用来进行数值计算

GC不会把uintptr当作指针,uintptr不会持有一个对象,uintptr类型的目标会被GC回收

unasfe

在Go中,unsafe是一个包,内容也比较简短,但注释非常多,这个包主要是用来在一些底层编程中,让你能够操作内存地址计算,也就是说Go本身是不支持指针运算,但还是留了一个后门,而且Go也不建议研发人员直接使用unsafe包的方法,因为它绕过了Go的内存安全原则,是不安全的,容易使你的程序出现莫名其妙的问题,不利于程序的扩展与维护但为什么说它呢,因为很多框架包括SDK中的源代码都用到了这个包的知识,在看源代码时这块不懂,容易懵。下面看看这个包定义了什么?

//ArbitraryType的类型也是int,但它被赋予特殊的含义,代表一个Go的任意表达式类型
type ArbitraryType int //Pointer是一个int指针类型,在Go种,它是所有指针类型的父类型,也就是说所有的指针类型都可以转化为Pointer, uintptr和Pointer可以相互转化
type Pointer *ArbitraryType //返回指针变量在内存中占用的字节数(记住,不是变量对应的值占用的字节数)
func Sizeof(x ArbitraryType) uintptr /*Offsetof返回变量指定属性的偏移量,这个函数虽然接收的是任何类型的变量,但是有一个前提,就是变量要是一个struct类型,且还不能直接将这个struct类型的变量当作参数,只能将这个struct类型变量的属性当作参数*/
func Offsetof(x ArbitraryType) uintptr //返回变量对齐字节数量
func Alignof(x ArbitraryType) uintptr

什么是内存对齐?为什么要内存对齐?

在我了解比较深入的语言中(Java Go)都有内存对齐的概念,百度百科对内存对齐的概念是这样定义的:“内存对齐”应该是编译器的“管辖范围”。编译器为程序中的每个“数据单元”安排在适当的位置上,所谓的数据单元其实就是变量的值。

为什么要内存对齐呢?

  1. 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常(32位平台上运行64位平台上编译的程序要求必须8字节对齐,否则发生panic)
  2. 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问

对齐规则:也就是对齐的边界,多少个字节内存对齐,在32位操作系统上,是4个自己,在64位操作系统上是8个字节

通过一幅图来理解上面的内容,下图只是举个例子,位数并没有画全

指针运算和内存对齐实践

内存对齐实践

理论总是枯燥的,但必须了解,也许看了理论还是不懂,接下来通过实践让你明白

//创建一个变量
var i int8 = 10 //建一个变量转化成Pointer 和 uintptr
p := unsafe.Pointer(&i) //入参必须是指针类型的
fmt.Println(p) //是内存地址0xc0000182da
u := uintptr(i)
fmt.Println(u) //结果就是10 //Pointer转换成uintptr
temp := uintptr(p)
//uintptr转Pointer
p= unsafe.Pointer(u) //获取指针大小
u = unsafe.Sizeof(p) //传入指针,获取的是指针的大小
fmt.Println(u) // 打印u是:8
//获取的是变量的大小
u = unsafe.Sizeof(i)
fmt.Println(u) //打印u是:1 //创建两个个结构体
type Person1 struct{
a bool
b int64
c int8
d string
}
type Person2 struct{
b int64
c int8
a bool
d string
}
//接下来演示一下内存对齐,猜一猜下面l两个打印值是多少呢?
person1 := Person1{a:true,b:1,c:1,d:"spw"}
fmt.Println(unsafe.Sizeof(person1))
person2 := Person2{b:1,c:1,a:true,d:"spw"}
fmt.Println(unsafe.Sizeof(person2))
//第一个结果是40,第二个结果是32,为什么会有这些差距呢?其实就是内存对齐做的鬼,我来详细解释一下

我们知道在Person1和Person2种变量类型都一样,只是顺序不太一样,

bool占1个字节,

int64占8个字节,

int8占一个字节,

string占用16个字节,

总的结果应该是 1+8+1+16= 26,为啥Person1是40呢,Person2是32,看下图

根据上图,我们就明白了,在结构体编写中存在内存对齐的概念,而且我们应该小心,尽可能的避免因内存对齐导致结构体大小增大,在书写过程中应该让小字节的变量挨着。我们可以工具进行检测(golangci-lint)。

我们可以通过func Alignof(x ArbitraryType) uintptr这个方法返回内存对齐的字节数量,如下代码

type Person1 struct{
a bool
b int64
c int8
d string
}
p := Person{a:true,b:1,c:1,d:"spw"}
fmt.Println(unsafe.Alignof(person))
type Person2 struct{
a bool
c int8
}
p1 := Person1{a:true,b:1,c:1,d:"spw"}
fmt.Println(unsafe.Alignof(p1))
p2 := Person2{a:true,c:1}
fmt.Println(unsafe.Alignof(p2))
//你任务上面两个println打印多少呢?结果是8,1,在结构体中,内存对齐是按照结构体中最大字节数对齐的(但不会超过8)
指针运算实践

我们还是用代码来举例说明


type W struct {
b int32
c int64
}
var w *W = new(W)
//这时w的变量打印出来都是默认值0,0
fmt.Println(w.b,w.c) //现在我们通过指针运算给b变量赋值为10
b := unsafe.Pointer(uintptr(unsafe.Pointer(w)) + unsafe.Offsetof(w.b))
*((*int)(b)) = 10
//此时结果就变成了10,0
fmt.Println(w.b,w.c)

解释一下上面的代码

uintptr(unsafe.Pointer(w))获取了w的指针起始值,

unsafe.Offsetof(w.b) 获取b变量的偏移量

两个相加就得到了b的地址值,将通用指针Pointer转换成具体指针((*int)(b)),通过 * 符号取值,然后赋值,((int)(b)) 相当于把(*int) 转换成 int了,最后对变量重新赋值成10,这样指针运算就完成了。

![](https://img2018.cnblogs.com/blog/706455/201909/706455-20190911210708072-261554801.jpg)

Go的内存对齐和指针运算详解和实践的更多相关文章

  1. C#中缓存的使用 ajax请求基于restFul的WebApi(post、get、delete、put) 让 .NET 更方便的导入导出 Excel .net core api +swagger(一个简单的入门demo 使用codefirst+mysql) C# 位运算详解 c# 交错数组 c# 数组协变 C# 添加Excel表单控件(Form Controls) C#串口通信程序

    C#中缓存的使用   缓存的概念及优缺点在这里就不多做介绍,主要介绍一下使用的方法. 1.在ASP.NET中页面缓存的使用方法简单,只需要在aspx页的顶部加上一句声明即可:  <%@ Outp ...

  2. c++中内存拷贝函数(C++ memcpy)详解

    原型:void*memcpy(void*dest, const void*src,unsigned int count); 功能:由src所指内存区域复制count个字节到dest所指内存区域. 说明 ...

  3. [转载]windows任务管理器中的工作设置内存,内存专用工作集,提交大小详解

    windows任务管理器中的工作设置内存,内存专用工作集,提交大小详解 http://shashanzhao.com/archives/832.html 虽然是中文字,但是理解起来还是很困难,什么叫工 ...

  4. Opencv中Mat矩阵相乘——点乘、dot、mul运算详解

    Opencv中Mat矩阵相乘——点乘.dot.mul运算详解 2016年09月02日 00:00:36 -牧野- 阅读数:59593 标签: Opencv矩阵相乘点乘dotmul 更多 个人分类: O ...

  5. C_C++指针指针应用详解

    前言:复杂类型说明 要了解指针,多多少少会出现一些比较复杂的类型,所以我先介绍一下如何完全理解一个复杂类型,要理解复杂类型其实很简单,一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级,其 ...

  6. 小甲鱼PE详解之区块描述、对齐值以及RVA详解(PE详解06)

    各种区块的描述: 很多朋友喜欢听小甲鱼的PE详解,因为他们觉得课堂上老师讲解的都是略略带过,绕得大家云里雾里~刚好小甲鱼文采也没课堂上的教授讲的那么好,只能以比较通俗的话语来给大家描述~ 通常,区块中 ...

  7. C++ this指针的详解

    C++中this指针的用法详解   转自:http://blog.chinaunix.net/uid-21411227-id-1826942.html 1. this指针的用处: 一个对象的this指 ...

  8. C++内存使用机制基本概念详解

    .程序使用内存区 一个程序占用的内存区一般分为5种: ()全局.静态数据区:存储全局变量及静态变量(包括全局静态变量和局部静态变量) ()常量数据区:存储程序中的常量字符串等. ()代码区:存储程序的 ...

  9. C语言结构体指针(指向结构体的指针)详解

    C语言结构体指针详解 一.前言 一个指向结构体的变量的指针表示的是这个结构体变量占内存中的起始位置,同样它也可以指向结构体变量数组. *a).b 等价于 a->b. "."一 ...

随机推荐

  1. UA判断打开页面的环境,然后在callBack写相应环境下的回调函数

    这是js代码 /* * 2016.11.10 * SunJingxin * V 1.0.0 * */ (function(){ /* * 使用方法: * 一.引入ua.js * 二.直接调用 Mobi ...

  2. python基础十一之装饰器进阶

    函数的双下划线方法 def hahahha(): """测试函数""" print('zxc') print(hahahha.__name_ ...

  3. 原 在windows上创建文件名以“.”开头的文件

    在实际项目开发中,我们会经常遇到以”.”开头的配置文件.例如我们如果需要通过配置文件给npm更换源就需要更改.npmrc文件,然而还有很多友友还不清楚如何创建一个类似的文件,今天就和大家分享下这个小技 ...

  4. H3C 端口绑定基本配置

  5. Linux 内核PCI 中断

    对于中断, PCI 是容易处理的. 在 Linux 启动时, 计算机的固件已经分配一个唯一的中 断号给设备, 并且驱动只需要使用它. 中断号被存储于配置寄存器 60 (PCI_INTERRUPT_LI ...

  6. 2019-6-23-win10-uwp-应用放到桌面

    title author date CreateTime categories win10 uwp 应用放到桌面 lindexi 2019-06-23 11:11:30 +0800 2019-06-2 ...

  7. koa2--08.koa-session的使用

    首先安装 koa-session中间件 //koa-session的使用 const koa = require('koa'); var router = require('koa-router')( ...

  8. Centos7网络连接不上:Network is unreachable 解决方案

    有朋友的centos7装在虚拟机上挂起后在打开不能正常连接网络,我的也出现了这个问题,试着用dhclient重新分配一下地址,无奈系统提示dhclient正在运行,没办法只能试试其它办法,之后研究了一 ...

  9. LDO

    1.出现原因:便携式设备的发展,使得人们对电源的要求越来越高,而以前一直使用的三段稳压电源无法满足需求. 2.特点:内部的PNP管导通压降很小,自耗很低. 3.应用场合: (1)不同电压输出级别的应用 ...

  10. 第二阶段:2.商业需求分析及BRD:7.商业需求文档3

    BRD模版 阐述需求来源以及调研分析情况 百度指数工具.定量的数据.发展趋势,是否与公司的战略冲突.环境政策:比如做内容的运营. 决策层看重的! 第二大块. 通过什么方式解决这个需求. 规划能力.类似 ...