《快学 Go 语言》第 14 课 —— 魔术变性指针

YOLOv8改进:卷积篇 | 保姆级探索动态蛇形卷积 (Dynamic Snake Convolution) 的实现与应用 动态蛇形卷积是一种新的卷积操作,它能够更好地捕捉图像中的细节信息。传统卷积核的形状和大小是固定的,而动态蛇形卷积则允许卷积核在不同的位置动态调整其形状,以更好地适应输入数据的特点。这种灵活性使得动态蛇形卷积在处理复杂背景和细节丰富的图像时具有显著优势。我们先定义一个基本的动态蛇形卷积模块。这个模块将作为YOLOv8卷积层的替代。为了全面集成动态蛇形卷积,我们需要修改YOLOv8的模型定义,将所有的基础卷积层替换为我们的SnakeConv模块。 阅读详情

本节我们要学习一些 Go 语言的魔法功能,通过内置的 unsafe 包提供的功能,直接操纵指定内存地址的内存。有了 unsafe 包,我们就可以洞悉 Go 语言内置数据结构的内部细节。

unsafe.Pointer

Pointer 代表着变量的内存地址,可以将任意变量的地址转换成 Pointer 类型,也可以将 Pointer 类型转换成任意的指针类型,它是不同指针类型之间互转的中间类型。Pointer 本身也是一个整型的值。

type Pointer int


640?wx_fmt=png

图片

在 Go 语言里不同类型之间的转换是要受限的。普通的基础变量转换成不同的类型需要进行内存浅拷贝,而指针变量类型之间是禁止直接转换的。要打破这个限制,unsafe.Pointer 就可以派上用场,它允许任意指针类型的互转。

指针的加减运算

Pointer 虽然是整型的,但是编译器禁止它直接进行加减运算。如果要进行运算,需要将 Pointer 类型转换 uintptr 类型进行加减,然后再将 uintptr 转换成 Pointer 类型。uintptr 其实也是一个整型。

type uintptr int


640?wx_fmt=png

图片

下面让我们就来尝试一下刚刚学到的魔法

package main

import "fmt"
import "unsafe"

type Rect struct {
    Width int
    Height int
}

func main() {
    var r = Rect {50, 50}
    // *Rect => Pointer => *int => int
    var width = *(*int)(unsafe.Pointer(&r))
    // *Rect => Pointer => uintptr => Pointer => *int => int
    var height = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&r)) + uintptr(8)))
    fmt.Println(width, height)
}

------
50 50

package main

import "fmt"
import "unsafe"

type Rect struct {
    Width int
    Height int
}

func main() {
    var r = Rect {50, 50}
    // var pw *int
    var pw = (*int)(unsafe.Pointer(&r))
    // var ph *int
    var ph = (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&r)) + uintptr(8)))
    *pw = 100
    *ph = 100
    fmt.Println(r.Width, r.Height)
}

--------
100 100

var ph = (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&r)) + unsafe.Offsetof(r.Height))

探索切片内部结构

在切片小节,我们知道了切片分为切片头和内部数组两部分,下面我们使用 unsafe 包来验证一下切片的内部数据结构,看看它和我们预期的是否一样。

package main

import "fmt"
import "unsafe"

func main() {
    // head = {address, 10, 10}
    // body = [1,2,3,4,5,6,7,8,9,10]
    var s = []int{1,2,3,4,5,6,7,8,9,10}
    var address = (**[10]int)(unsafe.Pointer(&s))
    var len = (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8)))
    var cap = (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(16)))
    fmt.Println(address, *len, *cap)
    var body = **address
    for i:=0; i< len(body); i++ {
        fmt.Printf("%d ", body[i])
    }
}

------------------
0xc42000a080 10 10
1 2 3 4 5 6 7 8 9 10

640?wx_fmt=png
图片

字符串与字节切片的高效转换

在字符串小节我们提到字节切片和字符串之间的转换需要复制内存,如果字符串或者字节切片的长度较大,转换起来会有较高的成本。下面我们通过 unsafe 包提供另一种高效的转换方法,让转换前后的字符串和字节切片共享内部存储。

640?wx_fmt=png
图片

字符串和字节切片的不同点在于头部,字符串的头部 2 个 int 字节,切片的头部 3 个 int 字节

package main

import "fmt"
import "unsafe"

func main() {
    fmt.Println(bytes2str(str2bytes("hello")))
}

func str2bytes(s string) []byte {
    var strhead = *(*[2]int)(unsafe.Pointer(&s))
    var slicehead [3]int
    slicehead[0] = strhead[0]
    slicehead[1] = strhead[1]
    slicehead[2] = strhead[1]
    return *(*[]byte)(unsafe.Pointer(&slicehead))
}

func bytes2str(bs []byte) string {
    return *(*string)(unsafe.Pointer(&bs))
}

-----
hello

深入接口变量的赋值

在接口变量的小节,有一个问题还悬而未决,那就是接口变量在赋值时发生了什么?

640?wx_fmt=png
图片

通过 unsafe 包,我们就可以看清里面的细节,下面我们将一个结构体变量赋值给接口变量,看看修改结构体的内存会不会影响到接口变量的数据内存

package main

import "fmt"
import "unsafe"

type Rect struct {
    Width int
    Height int
}

func main() {
    var r = Rect{50, 50}
    // {typeptr, dataptr}
    var s interface{} = r

    var sptrs = *(*[2]*Rect)(unsafe.Pointer(&s))
    // var dataptr *Rect
    var sdataptr = sptrs[1]
    fmt.Println(sdataptr.Width, sdataptr.Height)

    // 修改原对象,看看接口指向的对象是否受到影响
    r.Width = 100
    fmt.Println(sdataptr.Width, sdataptr.Height)
}

-------
50 50
50 50

package main

import "fmt"
import "unsafe"

type Rect struct {
    Width int
    Height int
}

func main() {
    // {typeptr, dataptr}
    var s interface{} = Rect{50, 50}
    var r = s

    var rptrs = *(*[2]*Rect)(unsafe.Pointer(&r))
    var rdataptr = rptrs[1]
    var sptrs = *(*[2]*Rect)(unsafe.Pointer(&s))
    var sdataptr = sptrs[1]

    fmt.Println(sdataptr.Width, sdataptr.Height)
    fmt.Println(rdataptr.Width, rdataptr.Height)

    // 修改原对象
    sdataptr.Width = 100
    // 再对比一下原对象和目标对象
    fmt.Println(sdataptr.Width, sdataptr.Height)
    fmt.Println(rdataptr.Width, rdataptr.Height)
}

-----------
50 50
50 50
100 50
100 50

package main

import "fmt"
import "unsafe"

type Areable interface {
    Area() int
}

type Rect struct {
    Width int
    Height int
}

func (r Rect) Area() int {
    return r.Width * r.Height
}

func main() {
    // {typeptr, dataptr}
    var s Areable = Rect{50, 50}
    var r interface{} = s

    var rptrs = *(*[2]*Rect)(unsafe.Pointer(&r))
    var rdataptr = rptrs[1]
    var sptrs = *(*[2]*Rect)(unsafe.Pointer(&s))
    var sdataptr = sptrs[1]

    fmt.Println(sdataptr.Width, sdataptr.Height)
    fmt.Println(rdataptr.Width, rdataptr.Height)

    // 修改原对象
    sdataptr.Width = 100
    // 再对比一下原对象和目标对象
    fmt.Println(sdataptr.Width, sdataptr.Height)
    fmt.Println(rdataptr.Width, rdataptr.Height)
}

------
50 50
50 50
100 50
100 50

package main

import "fmt"
import "unsafe"

type Areable interface {
    Area() int
}

type Rect struct {
    Width int
    Height int
}

func (r Rect) Area() int {
    return r.Width * r.Height
}

func main() {
    // {typeptr, dataptr}
    var s interface{} = Rect{50, 50}
    var r Areable = s.(Areable)

    var rptrs = *(*[2]*Rect)(unsafe.Pointer(&r))
    var rdataptr = rptrs[1]
    var sptrs = *(*[2]*Rect)(unsafe.Pointer(&s))
    var sdataptr = sptrs[1]

    fmt.Println(sdataptr.Width, sdataptr.Height)
    fmt.Println(rdataptr.Width, rdataptr.Height)

    // 修改原对象
    sdataptr.Width = 100
    // 再对比一下原对象和目标对象
    fmt.Println(sdataptr.Width, sdataptr.Height)
    fmt.Println(rdataptr.Width, rdataptr.Height)
}

------
50 50
50 50
100 50
100 50

package main

import "fmt"
import "unsafe"

type Areable interface {
    Area() int
}

type Rect struct {
    Width int
    Height int
}

func (r Rect) Area() int {
    return r.Width * r.Height
}

func main() {
    // {typeptr, dataptr}
    var s interface{} = Rect{50, 50}
    var r Rect = s.(Rect)

    var sptrs = *(*[2]*Rect)(unsafe.Pointer(&s))
    var sdataptr = sptrs[1]

    // 修改原对象
    sdataptr.Width = 100
    // 再对比一下原对象和目标对象
    fmt.Println(sdataptr.Width, sdataptr.Height)
    fmt.Println(r.Width, r.Height)
}

从上面 5 个 问题,我们可以得出结论,接口类型和结构体类型似乎是两个不同的世界。只有接口类型之间的赋值和转换会共享数据,其它情况都会复制数据,其它情况包括结构体之间的赋值,结构体转接口,接口转结构体。不同接口变量之间的转换本质上只是调整了接口变量内部的类型指针,数据指针并不会发生改变。

通过 unsafe 包我们还可以分析很多细节,在高级内容部分,我们将会频繁使用这个工具。

640?wx_fmt=png

扫一扫二维码,订阅《快学 Go 语言》更多内容

「深度学习之优化算法」笔记(二)优化算法的分类 但是由于生物的局限性,人们所创造出的算法也会有局限性:我们所熟知的生物都生存在三维空间,在这些环境中,影响生物生存的条件比较有限,反应到算法中就是这些算法在解决较低维度的问题时效果很好,当遇到超高维(维度>500)问题时,结果可能不容乐观,没做过实验,我也不敢乱说。当然,他们的目的不是在最佳狩猎位置集合,他们的目的是在过去的途中看是否能发现更加好的狩猎位置,去往已经到过的狩猎地点再次狩猎是没有意义的,因为每个位置获取食物的难易程度是固定的。如果他们的策略是“不跟随最优解”,那么他们的策略是什么呢? 阅读详情

相关推荐

【汽车ECU电控数据管理篇】A2L文件在ECU标定中的关键作用与实战解析

本文深入解析A2L文件在汽车ECU标定中的关键作用,详细介绍了其作为'数据字典'的核心功能与实战应用。通过剖析A2L文件的结构、关键标签及常见问题,帮助工程师高效管理电控数据,提升ECU标定效率与准确性。文章特别强调了ASAP2标准下的A2L文件在汽车电控系统中的重要性。

a3b4c5的博客 789

必撸系列!Go另外几个黑魔法技巧汇总

导语|最近一段时间,笔者重新梳理了一下go知识点,并深入地看看了它的源码,在实践中又有了新的沉淀,于是写下这篇文章和大家分享一下。在上一篇文章《来了!Go的2个黑魔法技巧》中,笔者分享了go中两个有意思的技巧。而最近一段时间,笔者重新梳理了一下go知识点,并深入地看看了它的源码,在实践中又有了新的沉淀,于是写下这篇文章和大家分享一下。一、魔法:最小化运行时(mini...

QcloudCommunity的博客 1798

从零开始搭建企业级安全扫描平台:基于Docker的Greenbone和ClamAV部署指南

本文提供了一份基于Docker容器技术,从零开始搭建企业级安全扫描平台的实战指南。详细介绍了如何部署和集成漏洞扫描工具Greenbone与病毒扫描工具ClamAV,涵盖架构设计、Docker环境优化、容器化部署步骤以及平台集成与监控方案,帮助企业构建自动化、可扩展的安全防御核心组件。

delta的博客 759

来了!Go的2个黑魔法技巧

导语|最近,在写Go代码的时候,我发现了其特别有意思的两个奇技淫巧或者黑魔法,若使用得好可以提升性能,用得不好就会招来恶魔,嘿嘿,于是写下这篇文章和大家分享一下。一、魔法:调用runtime中的私有函数按照Go的编译约定,代码包内以小写字母开头的函数、变量是私有的:package test // 私有 func abs() {} // 公共 func Abs(...

QcloudCommunity的博客 851

Simulink+Simscape多体仿真:六自由度Stewart平台动力学建模全流程

本文详细介绍了使用Simulink和Simscape Multibody进行六自由度Stewart平台动力学建模的全流程,涵盖从CAD模型导入、运动学逆解计算到实时控制系统的设计。通过MATLAB/Simulink与Simscape的协同工作,实现高保真度的并联机器人仿真,适用于飞行模拟器、精密加工等高精度领域。

r2s3t4的博客 489

WRF模型下中国土地利用数据替换USGS-24分类的完整实践指南

本文提供了在WRF模型中,将中国土地利用数据替换默认USGS-24分类的完整实践指南。详细阐述了替换下垫面数据的必要性,并分步讲解了数据准备、坐标转换、分类体系重映射、格式转换及配置文件修改等核心操作,旨在帮助用户提升针对中国区域的模拟精度。

salt的博客 777

go unsafe常见应用

go 本身是类似java一样,底层运行虚拟机,整个程序处于托管状态(GC等),要想和底层打交道需要用到unsafe模块,unsafe也常用于性能提升和程序灵活处理场景,本文介绍unsafe常用的几个场景。

文洲的专栏 1128

研一脑电小白日记 之 EEGLAB预处理实战指南

本文为脑电研究新手提供了一份详尽的EEGLAB预处理实战指南。从MATLAB与EEGLAB环境搭建、使用NeuracleEEGFileReader插件导入BDF格式数据开始,逐步讲解了电极定位、滤波、降采样、ICA去伪迹等核心步骤,旨在帮助初学者高效完成脑电数据预处理,为后续分析奠定坚实基础。

weixin_29230649的博客 563

腾讯php面试题总结(一) 15个魔术方法的总结

腾讯php面试题总结(一)  PHP5中魔术方法函数有哪几个,请举例说明各自的用法   __wakeup   __construct, __destruct __call, __get, __set, __isset, __unset __sleep,  __toString, __set_state, __clone __autoload  1.__autoload() 类文件自动加载

技术小黑屋 5929

路径跟踪 | 图解分析人工势场算法APF(附ROS C++/Python/Matlab仿真)

人工势场法的基本思想是在机器人运动环境中创建一个势场,再搜索一条势函数下降的方向,寻找一条无碰撞的最优路径。本文介绍APF算法的基本原理,并提供势场可视化实现,基于ROS C++/Python/Matlab进行算法仿真实验加深理解

FRIGIDWINTER的博客 7396

【HALCON工业视觉应用探索】10. C#+Halcon 3D视觉实战:点云处理与三维测量工业指南(附完整代码与实操案例)

摘要:本文聚焦C#+Halcon在3D视觉领域的工业应用,从新手友好的“基础3D检测系统”切入,逐步深入“汽车铸件三维测量”等工业级场景。内容涵盖核心概念(点云数据结构、3D采集原理、坐标系统)、完整技术流程(数据采集→预处理→特征提取→三维测量→结果可视化)、工业级代码实现(多视角点云融合、微缺陷检测、高精度尺寸测量),以及现场落地关键技巧(硬件选型、标定流程、抗干扰优化)。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 2006

Go / C / Python / Java 四语言对照学习总览

按统一维度横向对比四门语言的核心知识点,便于交叉记忆与迁移学习。

2401_83227843的博客 957

SAP MD81创建客户独立需求简介

正常我们用的最多的计划独立需求都是使用的是MD61 ,今天我们说下SAP的另外的一个标准功能客户独立需求MD81。虽然SAP给这个TCODE的描述是客户独立需求,但是实际是没有地方去关联对应的客户信息的。可以看到根据客户需求产生了对应的计划订单,我们创建的时候选择的KSV是按库存生产的,所以转换的时候生产订单也按单生产的。上面填写的这些字段的信息是不是和我们销售订单里面的字段的信息很相似。1)填写交货日期,可以选择按月 ,按日,按周等类型选择对应的日期。保存后会显示对应的客户独立需求的号码,如下图。

我小时候很黑的博客 2907

PHP 8.2枚举JSON序列化只读陷阱,99%新手都会踩的雷区

解决PHP 8.2枚举JSON序列化只读属性丢失问题,掌握正确序列化方法。适用于API开发场景,通过自定义jsonSerialize方法保留枚举值与标签,提升数据一致性。实用技巧值得收藏

CodePulse的博客 695

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

分布式系统技术难题--异地多活

什么是异地多活?为了保证系统能够对机房级别的故障进行容错,不会使系统不可用,这就需要在机房级别对系统进行冗余处理。而这就需要在架构上进行良好的设计。来面对多机房场景下的技...

codehole_的博客 570

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

12月19日云栖精选夜读 | 外行人都能看得懂的机器学习,错过了血亏!

前言 只有光头才能变强 没错,这篇主要跟大家一起入门机器学习。作为一个开发者,”人工智能“肯定是听过的。作为一个开发面试者,肯定也会见过”机器学习“这个岗位(反正我校招的时候就遇到过)。 可能还会听过或者见过“深度学习”、“神经网络”等等这些非常火的名词,那你对这些术语了解多少呢? 相信大家这几天在朋友圈也可以看到这照片: // 通过if else 以人工...

weixin_34272308的博客 202

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

12月19日云栖精选夜读 | 外行人都能看得懂的机器学习,错过了血亏! ...

前言 只有光头才能变强 没错,这篇主要跟大家一起入门机器学习。作为一个开发者,”人工智能“肯定是听过的。作为一个开发面试者,肯定也会见过”机器学习“这个岗位(反正我校招的时候就遇到过)。 可能还会听过或者见过“深度学习”、“神经网络”等等这些非常火的名词,那你对这些术语了解多少呢? 相信大家这几天在朋友圈也可以看到这照片: // 通过if else 以人工...

weixin_33858249的博客 220

Python深度学习:从入门到实战

亲爱的读者,欢迎您翻开这本书。我们即将探索的,是深度学习的宇宙——一个由数据、算法与算力构筑的奇妙世界。它既是严谨的科学,也是创造的艺术,更是一条通往未来智慧的修行之路。本书将带您从最基础的数学原理出发,亲手搭建神经网络,驾驭Transformer等前沿模型,最终将智慧转化为现实世界的价值。请放下畏惧,保持好奇。这不仅是一次知识的学习,更是一场思维的远行。来,随我一起,开启这趟非凡的旅程吧。

YunWisdom 163
上一篇: 为什么 Go 语言能在中国这么火?
下一篇: 一位非科班阿里程序员的 CTO 之路
码洞
博客等级 码龄10年 41粉丝 137原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值