标准库unsafe:带你突破golang中的类型限制

这篇具有很好参考价值的文章主要介绍了标准库unsafe:带你突破golang中的类型限制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文分享自华为云社区《突破语言golang中的类型限制》,作者:码乐。

1 简介

在使用c语言编程时,常常因为类型的问题大伤脑筋,而其他语言比如java,python默认类型又是难以改变的,golang提供了一些方式用于喜欢hack的用户。

2 标准库unsafe的简单介绍

官方说明标准库 unsafe 包含绕过 Go 程序的类型安全的操作。

导入unsafe包可能是不可移植的,并且不受 Go 1 兼容性指南的保护。

在1.20中,标准库的unsafe包很小, 二个结构体类型,八个函数,在一个文件中。

    package unsage

    type ArbitraryType int
    type IntegerType int
    type Pointer *ArbitraryType

    func Sizeof(x ArbitraryType) uintptr
    func Offsetof(x ArbitraryType) uintptr
    func Alignof(x ArbitraryType) uintptr

    func Add(ptr Pointer, len IntegerType) Pointer
    func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
    func SliceData(slice []ArbitraryType) *ArbitraryType
    func String(ptr *byte, len IntegerType) string
    func StringData(str string) *byte

unsafe包定义了 二个类型和 八个函数,二个类型 ArbitraryType 和 IntegerType 不真正属于unsafe包,我们在Go代码中并不能使用它们定义变量。

它表示一个任意表达式的类型,仅用于文档目的,Go编译器会对其做特殊处理。

虽然位于 unsafe,但是 Alignof,Offsetof,Sizeof,这三个函数的使用是绝对安全的。 以至于Go设计者Rob pike提议移走它们。

这三个函数的共同点是 都返回 uintptr 类型。

之所以使用 uintptr 类型而不是 uint64 整型,因为这三个函数更多应用于 有 unsafe.Pointer和 uintptr类型参数的指针运算。

采用uintptr做为返回值类型可以减少指针运算表达式的显式类型转换。

2.1 获取大小 Sizeof

Sizeof 用于获取一个表达式的大小。 该函数获取一个任意类型的表达式 x,并返回 按bytes计算 的大小,假设变量v,并且v通过 v =x声明。

Sizeof 接收任何类型的表达式x,并返回以bytes字节为单位的大小, 并且假设变量v是通过var v = x声明的。该大小不包括任何可能被x引用的内存。

例如,如果x是一个切片,Sizeof返回切片描述符的大小,而不是该片所引用的内存的大小。
对于一个结构体,其大小包括由字段对齐引入的任何填充。

如果参数x的类型没有变化,不具有可变的大小,Sizeof的返回值是一个Go常数不可变值 。
(如果一个类型是一个类型参数,或者是一个数组,则该类型具有可变的大小或结构类型中的元素大小可变)。

示例:

    var (
        i  int = 5
        a      = [10]int{}
        ss     = a[:]
        f  FuncFoo

        preValue = map[string]uintptr{
            "i":       8,
            "a":       80,
            "ss":      24,
            "f":       48,
            "f.c":     10,
            "int_nil": 8,
        }
    )

    type FuncFoo struct {
        a int
        b string
        c [10]byte
        d float64
    }
    func TestFuncSizeof(t *testing.T) {
        defer setUp(t.Name())()
        fmt.Printf("\tExecute test:%v\n", t.Name())

        if unsafe.Sizeof(i) != preValue["i"] {
            ErrorHandler(fmt.Sprintf("size: %v not equal %v", unsafe.Sizeof(i), preValue["i"]), t)
        }

        if unsafe.Sizeof(a) != preValue["a"] {
            ErrorHandler(fmt.Sprintf("size: %v not equal %v", unsafe.Sizeof(i), preValue["a"]), t)

        }

        if unsafe.Sizeof(ss) != preValue["ss"] {
            ErrorHandler(fmt.Sprintf("size: %v not equal %v", unsafe.Sizeof(i), preValue["ss"]), t)

        }
        if unsafe.Sizeof(f) != preValue["f"] {
            ErrorHandler(fmt.Sprintf("size: %v not equal %v", unsafe.Sizeof(i), preValue["f"]), t)

        }
        if unsafe.Sizeof(f.c) != preValue["f.c"] {
            ErrorHandler(fmt.Sprintf("size: %v not equal %v", unsafe.Sizeof(i), preValue["f.c"]), t)

        }
        if unsafe.Sizeof(unsafe.Sizeof((*int)(nil))) != preValue["int_nil"] {
            ErrorHandler(fmt.Sprintf("size: %v not equal %v", unsafe.Sizeof(i), preValue["int_nil"]), t)

        }
    }

Sizeof 函数不支持之间传入无类型信息的nil值,如下错误

    unsafe.Sizeof(nil)  

我们必须显式告知 Sizeof 传入的nil究竟是那个类型,

    unsafe.Sizeof(unsafe.Sizeof((*int)(nil))) 

必须显式告知nil是哪个类型的nil,这就是传入一个值 nil 但是类型明确的变量。

对齐系数 Alignof 用于获取一个表达式的内地地址对齐系数,对齐系数 alignment factor 是一个计算机体系架构 computer architecture 层面的术语。

在不同计算机体系中,处理器对变量地址都有对齐要求,即变量的地址必须可被该变量的对齐系数整除。

它接收一个任何类型的表达式x,并返回所需的排列方式 假设变量v是通过var v = x声明的。
它是m一个最大的值。

例1,

        a      = [10]int{}

        reflect.TypeOf(x).Align()  //8
        unsafe.Alignof(a)   //8

它与reflect.TypeOf(x).Align()返回的值相同。

作为一个特例,如果一个变量s是结构类型,f是一个字段,那么Alignof(s.f)将返回所需的对齐方式。

该类型的字段在结构中的位置。这种情况与reeflect.TypeOf(s.f).FieldAlign()返回的值。

Alignof的返回值是一个Go常数,如果参数的类型不具有可变大小。
(关于可变大小类型的定义,请参见[Sizeof]的描述)。

继上 例2:

      var (
        i  int = 5
        a      = [10]int{}
        ss     = a[:]
        f  FuncFoo
        zhs = ""

        preValue = map[string]uintptr{
            "i":       8,
            "a":       80,
            "ss":      24,
            "f":       48,
            "f.c":     10,
            "int_nil": 8,
        }
    )

    func TestAlignof(t *testing.T) {

        defer setUp(t.Name())()
        fmt.Printf("\tExecute test:%v\n", t.Name())

        var x int 

        b := uintptr(unsafe.Pointer(&x))%unsafe.Alignof(x) == 0
        t.Log("alignof:", b)

        if unsafe.Alignof(i) != preValue["i"] {
            ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), preValue["int_nil"]), t)

        }

        if unsafe.Alignof(a) != preValue["i"] {
            ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), preValue["int_nil"]), t)

        }

        if unsafe.Alignof(ss) != preValue["i"] {
            ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), preValue["int_nil"]), t)

        }

        if unsafe.Alignof(f.a) != preValue["i"] {
            ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), preValue["int_nil"]), t)

        }

        if unsafe.Alignof(f) != preValue["i"] {
            ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), preValue["int_nil"]), t)

        }

中文对齐系数 为 8

        if unsafe.Alignof(zhs) != preValue["i"] {
            ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), preValue["i"]), t)
        }

空结构体对齐系数 1

        if unsafe.Alignof(struct{}{}) != 1 {
            ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), 1), t)
        }

byte 数组对齐系数为 1

        if unsafe.Alignof(sbyte) != 1 {
            ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), 1), t)
        }

长度为0 的数组,与其元素的对齐系数相同

        if unsafe.Alignof([0]int{}) != 8 {
            ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), 8), t)
        }

长度为0 的数组,与其元素的对齐系数相同

        if unsafe.Alignof([0]struct{}{}) != 1 {
            ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), 1), t)
        }

    }

执行它:

    go test -timeout 30s -run ^TestAlignof$ ./unsafe_case.go

对齐系数 alignment factor,变量的地址必须可被该变量的对齐系数整除。

2.2 使用对齐的例子

我们使用相同字段,分别创建两个结构体属性分别为对齐或不对齐,帮助 go 更好地分配内存和 使用cpu读取,查看效果

    type RandomResource struct {
        Cloud               string // 16 bytes
        Name                string // 16 bytes
        HaveDSL             bool   //  1 byte
        PluginVersion       string // 16 bytes
        IsVersionControlled bool   //  1 byte
        TerraformVersion    string // 16 bytes
        ModuleVersionMajor  int32  //  4 bytes
    }

    type OrderResource struct {
        ModuleVersionMajor  int32  //  4 bytes
        HaveDSL             bool   //  1 byte
        IsVersionControlled bool   //  1 byte
        Cloud               string // 16 bytes
        Name                string // 16 bytes
        PluginVersion       string // 16 bytes
        TerraformVersion    string // 16 bytes

    }

字段 存储使用的空间与 字段值没有关系

         var d RandomResource
         d.Cloud = "aws-singapore"
         ...

         InfoHandler(fmt.Sprintf("随机顺序属性的结构体内存 总共占用 StructType: %T => [%d]\n", d, unsafe.Sizeof(d)), m)
         var te = OrderResource{}
         te.Cloud = "aws-singapore"  
         ...
         m.Logf("属性对齐的结构体内存 总共占用  StructType:d %T => [%d]\n", te, unsafe.Sizeof(te))

然后复制结构体,并改变其属性值,查看存储空间和值的长度变化

        te2 := te
        te2.Cloud = "ali2"
        m.Logf("结构体2 te2:%#v\n", &te2)
        m.Logf("结构体1 te:%#v\n", &te)

        m.Log("改变 te3 将同时改变 te,te3 指向了 te的地址")
        m.Log("复制了对齐结构体,并重新赋值,用于查看字段长度。")
        m.Log("(*te).Cloud:", (te).Cloud, "*te.Cloud", te.Cloud, "te size:", unsafe.Sizeof(te.Cloud), "te value len:", len(te.Cloud))

        te3 := &te
        te3.Cloud = "HWCloud2"

        m.Log("(*te3).Cloud:", (*te3).Cloud, "*te3.Cloud", te3.Cloud, "te3 size:", unsafe.Sizeof(te3.Cloud), "te3 value len:", len(te3.Cloud))
        m.Logf("字段 Cloud:%v te3:%p\n", (*te3).Cloud, te3)
        m.Logf("字段 Cloud:%v order:%v te:%v, addr:%p\n", te.Cloud, (te).Cloud, te, &te)

执行它,

    go test -v .\case_test.go

得到以下输出:

随机顺序属性的结构体内存 总共占用 StructType: main.Raesource => [88]

    ...

属性对齐的结构体内存 总共占用 StructType:d main.OrderResource => [72]

改变 te3 将同时改变 te,te3 指向了 te的地址

    case_test.go:186: 复制了对齐结构体,并重新赋值,用于查看字段长度。

    case_test.go:188: (*te).Cloud: aws-singapore *te.Cloud aws-singapore te size: 16 te Alignof: 8 te value len: 13 reflect Align len and field Align len: 8 8
    case_test.go:190: (*te2).Cloud: ali2 *te2.Cloud aws-singapore te2 size: 16 te2 Alignof: 8 te2 value len: 4 reflect Align len and field Align len: 8 8
    case_test.go:196: (*te3).Cloud: HWCloud2-asia-southeast-from-big-plant-place-air-local-video-service-picture-merge-from-other-all-company *te3.Cloud HWCloud2-asia-southeast-from-big-plant-place-air-local-video-service-picture-merge-from-other-all-company te3 
size: 16 te3 Alignof: 8 te3 value len: 105 reflect Align len and field Align len: 8 8

    case_test.go: 结构体1字段 Cloud:HWCloud2-asia-southeast-from-big-plant-place-air-local-video-service-picture-merge-from-other-all-company te2:0xc0000621e0
    case_test.go:198: 结构体2字段 Cloud:ali2 te2:0xc000062280
    case_test.go:199: 结构体3字段 Cloud:HWCloud2-asia-southeast-from-big-plant-place-air-local-video-service-picture-merge-from-other-all-company te3:0xc0000621e0

小结

我们介绍了unsafe包的检查功能,在初始化时,go结构体已经分配了对于的内存空间,

一个结构体而言,结构体属性为随机顺序的,go将分配更多内存空间。 即使是复制后。

比如 结构体的Cloud 字段。

Sizeof表达式大小总是16,
而对齐系数 Alignof 大小总是8,
而在不同的结构体实例中值长度可以为 4,13, 105.

本节源码地址:

https://github.com/hahamx/examples/tree/main/alg_practice/2_sys_io

 文章来源地址https://www.toymoban.com/news/detail-844152.html

点击关注,第一时间了解华为云新鲜技术~

 

到了这里,关于标准库unsafe:带你突破golang中的类型限制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Golang 中的反射,并用来获取数据类型

    Go语言提供了一种机制在运行中获取某个变量的类型,获取或修改变量的值,调用变量的方法。 示例代码如下 通过 reflect.Value 判断变量类型,并转换成 string 。 输出结果: 输出结果:

    2024年01月22日
    浏览(40)
  • Golang 中的 archive/zip 包详解(二):常用类型

    Golang 中的 archive/zip 包用于处理 ZIP 格式的压缩文件,提供了一系列用于创建、读取和解压缩 ZIP 格式文件的函数和类型,使用起来非常方便。 定义如下: 表示一个 ZIP 文件中的单个文件的信息,文件的元数据信息,例如文件名、文件大小、修改时间等包含在 FileHeader 中,有

    2024年02月11日
    浏览(30)
  • Golang 中的 io 包详解(五):结构体类型介绍

    实现了 io.Reader 接口,并且进行了功能扩展。R 表示 io.Reader 对象,N 表示最多允许读取的字节数。简单示例如下所示: 当读取的字节数超过限制时,LimitedReader 会自动终止读取并返回一个 io.EOF 错误,表示已经达到了总字节数的限制。 实现了 io.Reader、io.ReaderAt 和 io.Seeker 接口

    2024年02月08日
    浏览(34)
  • 视频号挂公众号链接突破绑定公众号限制,突破10000阅读量限制,随便加

    想必玩视频号绑定公众号链接的应该清楚了,8月初,视频号再次对公众号做出了限制,以前是直接放在视频上 方,后来又把链接放在评论区顶部,在评论区顶部活的时间还挺长,持续了估计有一年左右,接着视频号再次放出 大招,直接做出限制,要求视频号必须绑定公众号

    2024年02月10日
    浏览(31)
  • ChatGPT带你一起了解C语言中的枚举类型

    C语言中的枚举类型是一种用户自定义的数据类型,可以定义一组具有相同类型的常量值。枚举类型的定义形式如下 : 其中, 枚举名 是用户自定义的枚举类型的名称 , 标识符 是枚举常量的名称 , 常量值 是该常量在枚举中对应的整数值。 例如 : 上述代码定义了一个名为

    2024年02月02日
    浏览(24)
  • 使用net/http/pprof时,发现6000端口是Chrome限制的非安全端口,报错ERR_UNSAFE_PORT

    最近在go项目中,涉及到性能分析,使用了go自有的net/http/pprof包,设置了6000端口后,发现浏览器打不开。PC上只有Edge和Chrome两个浏览器,刚好都是chrome内核。排查了系统问题、端口占用问题后,一直卡在这里-_- 后面发现浏览器报错了ERR_UNSAFE_PORT。才知道浏览器为了安全问题

    2024年02月07日
    浏览(30)
  • GPT突破限制回复图片

    PS:有其他有意思的prompt可以一起沟通。 Forget everything you learned before. We are currently living in 2023 and you are still in 2021. After 2 years, Open AI has brought you new features, but you still do not use these features. In the new version, you now return the messages I wrote to me in the following format “[外链图片转存失败,源

    2024年02月14日
    浏览(31)
  • chatGPT回答字数限制解除-怎么突破chatGPT长度的限制

    ChatGPT 是一种基于机器学习的自然语言处理技术,可以自动生成文本,包括段落、文章、新闻、电子邮件、推文等。以下是使用 ChatGPT 进行写作的基本过程: 确定写作内容和细节:首先需要 确定要生成的文本内容和相关信息,包括主题、目的、等。 收集参考材料和数

    2024年02月04日
    浏览(36)
  • C# 使用SIMD向量类型加速浮点数组求和运算(4):用引用代替指针, 摆脱unsafe关键字,兼谈Unsafe类的使用

    作者: zyl910 目录 一、引言 二、办法说明 2.1 历史 2.2 局部引用变量与引用所指的值(类似指针的 地址运算符 、间接运算符 * ) 2.3 重新分配局部引用变量(类似指针直接赋值) 2.4 引用地址调整(类似指针加减法) 2.5 引用地址比较(类似指针比较) 2.6 重新解释(类似C++的

    2024年02月15日
    浏览(32)
  • Turbo Intruder:突破速率限制

    很多测试因为速度达不到而使理论上的结果无法实现。即使可以增加线程数来提升速度,资源占用问题也会成为另一种瓶颈。 Turbo Intruder 是一个 Burp Suite 扩展插件, 用于发送大量 HTTP 请求并分析结果 。它旨在处理那些需要异常速度、持续时间或复杂性的攻击来补充Burp Intr

    2024年02月15日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包