go关于string与[]byte再学深一点
目标:充分理解string与[]bytes零拷贝转换的实现
先回顾下string与[]byte的基本知识
1. string与[]byte的数据结构
reflect包中关于字符串的数据结构
// StringHeader is the runtime representation of a string.type StringHeader struct {Data uintptrLen int}
Data指向的是某个数组的首地址
len代表数组的长度。
uintptr是一种特殊指针,下文会具体介绍
说明
- string是一个8位的byte的集合,通常代表utf-8文本(但不一定都是)
- string可以为empty但不能是nil
- string的值是不能改变的(因为底层是数组)

reflect包中关于[]bytes的数据结构
type SliceHeader struct {Data uintptrLen intCap int}
Data指向的就是byte数组
[]byte是一个指向byte类型数组的slice
可以看到stringStruct与slice区别是cap,说明[]bytes的值是可变的,因为底层是切片。而string的值是不能改变的(因为底层是数组)
2. 基本数据结构的空间大小以及内存对齐
bl := truefmt.Println("size of bool:", unsafe.Sizeof(bl))// 1i := 10fmt.Println("size of int:", unsafe.Sizeof(i)) // 8i32 := int32(10)fmt.Println("size of int32:", unsafe.Sizeof(i32)) // 4i64 := int64(10)fmt.Println("size of int64:", unsafe.Sizeof(i64)) // 8str := "xxx"fmt.Println("size of str:", unsafe.Sizeof(str)) // 16type xstruct struct {a boolb int32c string}xx := xstruct{true, 10, "hello"}fmt.Println("size of xx.a:", unsafe.Sizeof(xx.a)) // 1fmt.Println("size of xx.b:", unsafe.Sizeof(xx.b)) // 4fmt.Println("size of xx.c:", unsafe.Sizeof(xx.c)) // 16fmt.Println("size of xx:", unsafe.Sizeof(xx)) // 不是1+4+16=21,而是24,为什么呢?由于字节对齐// xx.a为一个字节,而实际存储时会占用一个"对齐系数"也就是8字节(对于64位机器,"对齐系数"是8字节);// xx.b为四个字节,所以1+4=5,放一个对齐系数(8字节),其余剩余部分用0补充// xx.c为16个字节,刚好放满2个对齐系数(16字节)。所以8+16=24字节
从代码中总结常见类型变量占用空间:
bool占1个字节
int32占4个字节
int与int64 占4字节(64位机器)
string占16个字节,其中包含2部分,第一部分unsafe.pointer占8字节,第二部分len int占8字节
struct结构体占用的空间,计算时需要考虑字节对齐,字节对齐的好处是减少cpu访问memory的此次,cpu读取memory最小单位是一个字长(8字节),从而提供访问内存性能。(具体细节请问google)


3. unsafe.pointer与uintptr
string与[]byte结构的定义出现了uintptr,并且unsafe.pointer通常用于类型转换,下面具体介绍2中指针类型。
- unsafe.pointer与uintptr都是指针,但又不是普通指针,经查阅指针分为三种类型,分别有:
1. *类型: 这是最常用的指针,名叫普通指针类型,用于传递对象地址,不能进行指针运算。
2. unsafe.Pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算,不能读取内存存储的值(必须转换到某一类型的普通指针)。
3. uintptr:用于指针运算. 本质是存储 `指针地址` 的int类型
- unsafe.Pointer类型有四个重要描述:
(1)任何类型的指针都可以被转化为Pointer
(2)Pointer可以被转化为任何类型的指针
(3)uintptr可以被转化为Pointer
(4)Pointer可以被转化为uintptr
简而言之,unsafe.Pointer可以实现指针类型的转换,uintptr用于指针计算
下面看看Pointer的内部结构:
type Pointer *ArbitraryType// ArbitraryType is here for the purposes of documentation only and is not actually// part of the unsafe package. It represents the type of an arbitrary Go expression.type ArbitraryType int
ArbitraryType是int的一个别名,在Go中对ArbitraryType赋予特殊的意义。代表一个任意Go表达式类型。
- unsafe.Pointer的使用示例:
value1 := int32(10)value2 := int64(12)p := &value1fmt.Println(reflect.TypeOf(p)) // *int32fmt.Println(*p) // 10//p = &value2 // 错误。 无法将p指向&value2地址,因为p是*int32类型,&value2是*ini64类型unsPtr := unsafe.Pointer(&value2) // 将*int64先转换为unsafe.Pointer类型指针,此时unsPtr指向&value2(也就是value2的地址)p = (*int32)(unsPtr) //转换为*int32指针类型fmt.Println(reflect.TypeOf(p)) // *int32fmt.Println(*p) // 11
- uintptr的使用示例
type student struct {name stringage uint8}s := student{name: "tom", // string类型,16字节age: 18, // int8类型,1字节}uptr := (uintptr)(unsafe.Pointer(&s)) // uptr指向结构体的首地址uptr = uptr + 16 // uptr移动16字节,指向age的地址age := *(*int8)(unsafe.Pointer(uptr)) // 转换为*int8类型的指针fmt.Printf("age=%d\n", age) // 18
- 对uinitptr与unsafe.Pointer有简单了解后,再看结构体内存对齐示例
type student struct {name stringage uint8city string}s := student{name: "tom", // string类型,16字节age: 18, // int8类型,1字节; 需要做内存对齐,独占一个字长,本身占一个字节,其余7个字节填充city: "shenzhen", // string类型,16字节}
结构体的内存空间占用情况:

代码验证结构体占用空间的总大小,以及每个成员占用空间大小:
// 结构体占用空间fmt.Println("size of s:", unsafe.Sizeof(s)) // 40// 计算变量的地址fmt.Printf("address of s.name: %p\n", &s.name) // 0xc00008c030--->转换为十进制824634294320fmt.Printf("address of s.age: %p\n", &s.age) // 0xc00008c040--->转换为十进制824634294336fmt.Printf("address of s.city: %p\n", &s.city) // 0xc00008c048--->转换为十进制824634294344// 打印内部变量的相对字符串首地址的偏移量fmt.Printf("offset of s.age:%d\n", unsafe.Offsetof(s.name)) // 0fmt.Printf("offset of s.age:%d\n", unsafe.Offsetof(s.age)) // 16fmt.Printf("offset of s.age:%d\n", unsafe.Offsetof(s.city)) // 24,计算方法是24=16+8
下面我们把s字符串再进一步“打开”,探索一下字符串内部
先将s字符串转换为*reflect.StringHeader, 并查看字符串内部Data,Len的值
x := (*reflect.StringHeader)(unsafe.Pointer(&s)) // 转换为*reflect.StringHeaderfmt.Printf("x.Data: %v\n", x.Data) // 17603737,这就是Data变量保存的具体值,其实是一个内存地址fmt.Println("type of x.Data:", reflect.TypeOf(x.Data)) // uintptrfmt.Printf("&x.Data: %p\n", &x.Data) // 0xc000100030fmt.Printf("x.Len: %v\n", x.Len) // 3, 字符串"tom"的长度fmt.Printf("&x.Len: %p\n", &x.Len) // 0xc000100038, 说明38-30=8,表示x.Data占8个字节
- 使用uintptr,计算出x.city的地址,再获取改地址的值
uptr := (uintptr)(unsafe.Pointer(&s)) // uptr指向结构体的首地址uptr = uptr + 16 + 8 // uptr移动16+8字节,指向address的地址city := *(*string)(unsafe.Pointer(uptr)) // 转换为*string类型的指针,再用*获取改地址的值,也就是x.city的值fmt.Printf("city=%s\n", city) // shenzhen
4.string与[]bytes零拷贝的实现
最后,有了基础知识后,我们再看看string与[]bytes零拷贝的实现
string转换为[]byte
// 内存零拷贝方式类型转换
func stringtobyte(s string) []byte {// &s转换为*reflect.StringHeadervar sptr *reflect.StringHeadersptr = (*reflect.StringHeader)(unsafe.Pointer(&s))var b []byte// &b转换为*reflect.SliceHeaderbptr := (*reflect.SliceHeader)(unsafe.Pointer(&b))// 填充*reflect.SliceHeader内的Data,Len,Capbptr.Data = sptr.Databptr.Len = sptr.Lenbptr.Cap = sptr.Lenreturn b
}
为了编译理解,将转换过去用图示表示

[]byte转换为string
// 内存零拷贝方式类型转换
func bytetostring(b []byte) string {var bptr *reflect.SliceHeaderbptr = (*reflect.SliceHeader)(unsafe.Pointer(&b))var s stringsptr := (*reflect.StringHeader)(unsafe.Pointer(&s))sptr.Data = bptr.Datasptr.Len = bptr.Lenreturn s
}
// 转换方法二
// 转换方法二
func String2Bytes(s string) []byte {sh := (*[2]uintptr)(unsafe.Pointer(&s))bh := [3]uintptr{sh[0], sh[1], sh[1]}return *(*[]byte)(unsafe.Pointer(&bh))
}func String2Bytes2(s string) []byte {// &s转换为unsafe.Pointer类型的指针,再转换为指向定长为2的uintptr数组的指针sh := (*[3]uintptr)(unsafe.Pointer(&s))// sh是一个指针,不能直接转换为*[]byte的指针,先转换为unsafe.Pointer,再转换为*[]byte指针;最后*取出指针指向的内容return *(*[]byte)(unsafe.Pointer(sh))
}
相关文章:
go关于string与[]byte再学深一点
目标:充分理解string与[]bytes零拷贝转换的实现 先回顾下string与[]byte的基本知识 1. string与[]byte的数据结构 reflect包中关于字符串的数据结构 // StringHeader is the runtime representation of a string.type StringHeader struct {Data uintptrLen int} …...
Qt 实战(7)元对象系统 | 7.4、属性系统:深度解析与应用
文章目录 一、属性系统:深度解析与应用1、定义属性2、属性系统的作用3、属性系统工作原理(1)Q_PROPERTY宏(2)moc 的作用(3)属性在元对象中的注册 4、获取与设置属性4.1、QObject::property()与Q…...
Docker核心技术:容器技术要解决哪些问题
云原生学习路线导航页(持续更新中) 本文是 Docker核心技术 系列文章:容器技术要解决哪些问题,其他文章快捷链接如下: 应用架构演进容器技术要解决哪些问题(本文)Docker的基本使用Docker是如何实…...
sklearn中的增量学习:特征提取的艺术
sklearn中的增量学习:特征提取的艺术 在机器学习领域,特征提取是构建有效模型的关键步骤。然而,并非所有数据集都适合一次性加载到内存中进行处理,尤其是在处理大规模数据集时。Scikit-learn(sklearn)提供…...
PostgreSQL 中如何处理数据的唯一性约束?
🍅关注博主🎗️ 带你畅游技术世界,不错过每一次成长机会!📚领书:PostgreSQL 入门到精通.pdf 文章目录 PostgreSQL 中如何处理数据的唯一性约束?一、什么是唯一性约束二、为什么要设置唯一性约束…...
VAE论文阅读
在网上看到的VAE解释,发现有两种版本: 按照原来论文中的公式纯数学推导,一般都是了解生成问题的人写的,对小白很不友好。按照实操版本的,非常简单易懂,比如苏神的。但是却忽略了论文中的公式推导ÿ…...
【数据分享】2013-2022年我国省市县三级的逐月SO2数据(excel\shp格式\免费获取)
空气质量数据是在我们日常研究中经常使用的数据!之前我们给大家分享了2000——2022年的省市县三级的逐月PM2.5数据和2013-2022年的省市县三级的逐月CO数据(均可查看之前的文章获悉详情)! 本次我们分享的是我国2013——2022年的省…...
【Jmeter】记录一次Jmeter实战测试
Jmeter实战 1、需求2、实现2.1、新建线程组2.2、导入参数2.3、新建HTTP请求2.4、添加监听器2.5、结果 1、需求 查询某个接口在高并发场景下的响应时间(loadtime),需求需要响应在50ms以内,接下来用Jmeter测试一下 Jmeter安装见文章《Jemeter安装教程&am…...
volatile,最轻量的同步机制
目录 一、volatile 二、如何使用? 三、volatile关键字能代替synchronized关键字吗? 四、总结: 还是老样子,先来看一段代码: 我们先由我们自己的常规思路分析一下代码:子线程中,一直循环&…...
在Linux、Windows和macOS上释放IP地址并重新获取新IP地址的方法
文章目录 LinuxWindowsmacOS 在Linux、Windows和macOS上释放IP地址并重新获取新IP地址的方法各有不同。以下是针对每种操作系统的详细步骤: Linux 使用DHCP客户端:大多数Linux发行版都使用DHCP(动态主机配置协议)来自动获取IP地址…...
Mamba-yolo|结合Mamba注意力机制的视觉检测
一、本文介绍 PDF地址:https://arxiv.org/pdf/2405.16605v1 代码地址:GitHub - LeapLabTHU/MLLA: Official repository of MLLA Demystify Mamba in Vision: A Linear AttentionPerspective一文中引入Baseline Mamba,指明Mamba在处理各种高…...
语音识别标记语言(SSML):自动标识中文多音字
好的,以下是完整的实现代码,包括导入库、分词、获取拼音和生成 SSML 标记的全过程: import thulac from pypinyin import pinyin, Style# 初始化 THULAC thu1 thulac.thulac(seg_onlyTrue)# 测试文本 text "银行行长正在走行。"…...
排序算法与复杂度介绍
1. 排序算法 1.1 排序算法介绍 排序也成排序算法(Sort Algorithm),排序是将一组数据,依照指定的顺序进行排序的过程 1.2 排序的分类 1、内部排序: 指将需要处理的所有数据都加载到**内部存储器(内存&am…...
Kafka介绍及Go操作kafka详解
文章目录 Kafka介绍及Go操作kafka详解项目背景解决方案面临的问题业界方案ELKELK方案的问题日志收集系统架构设计架构设计组件介绍将学到的技能消息队列的通信模型点对点模式 queue发布/订阅 topicKafka介绍Kafka的架构图工作流程选择partition的原则ACK应答机制Topic和数据日志…...
DAY05 CSS
文章目录 1 CSS选择器(Selectors)8. 后代(包含)选择器9. 直接子代选择器10. 兄弟选择器11. 相邻兄弟选择器12. 属性选择器 2 伪元素3 CSS样式优先级1. 相同选择器不同样式2. 相同选择器相同样式3. 继承现象4. 选择器不同权值的计算 4 CSS中的值和单位1. 颜色表示法2. 尺寸表示法…...
HTTPS 的加密过程 详解
HTTP 由于是明文传输,所以安全上存在以下三个风险: 窃听风险,比如通信链路上可以获取通信内容。篡改风险,比如通信内容被篡改。冒充风险,比如冒充网站。 HTTPS 在 HTTP 与 TCP 层之间加入了 SSL/TLS 协议,…...
spring整合mybatis,junit纯注解开发(包括连接druid报错的所有解决方法)
目录 Spring整合mybatis开发步骤 第一步:创建我们的数据表 第二步:编写对应的实体类 第三步:在pom.xml中导入我们所需要的坐标 spring所依赖的坐标 mybatis所依赖的坐标 druid数据源坐标 数据库驱动依赖 第四步:编写SpringC…...
ClusterIP、NodePort、LoadBalancer 和 ExternalName
Service 定义 在 Kubernetes 中,由于Pod 是有生命周期的,如果 Pod 重启它的 IP 可能会发生变化以及升级的时候会重建 Pod,我们需要 Service 服务去动态的关联这些 Pod 的 IP 和端口,从而使我们前端用户访问不受后端变更的干扰。 …...
【Day1415】Bean管理、SpringBoot 原理、总结、Maven 高级
0 SpringBoot 配置优先级 从上到下 虽然 springboot 支持多种格式配置文件,但是在项目开发时,推荐统一使用一种格式的配置 (yml是主流) 1 Bean管理 1.1 从 IOC 容器中获取 Bean 1.2 Bean 作品域 可以通过注解 Scope("proto…...
Git之repo sync -c与repo sync -dc用法区别(四十八)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
android RelativeLayout布局
<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...
基于鸿蒙(HarmonyOS5)的打车小程序
1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...
Pydantic + Function Calling的结合
1、Pydantic Pydantic 是一个 Python 库,用于数据验证和设置管理,通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发(如 FastAPI)、配置管理和数据解析,核心功能包括: 数据验证:通过…...
Linux中INADDR_ANY详解
在Linux网络编程中,INADDR_ANY 是一个特殊的IPv4地址常量(定义在 <netinet/in.h> 头文件中),用于表示绑定到所有可用网络接口的地址。它是服务器程序中的常见用法,允许套接字监听所有本地IP地址上的连接请求。 关…...
break 语句和 continue 语句
break语句和continue语句都具有跳转作用,可以让代码不按既有的顺序执行 break break语句用于跳出代码块或循环 1 2 3 4 5 6 for (var i 0; i < 5; i) { if (i 3){ break; } console.log(i); } continue continue语句用于立即终…...
联邦学习带宽资源分配
带宽资源分配是指在网络中如何合理分配有限的带宽资源,以满足各个通信任务和用户的需求,尤其是在多用户共享带宽的情况下,如何确保各个设备或用户的通信需求得到高效且公平的满足。带宽是网络中的一个重要资源,通常指的是单位时间…...
