面试官:你能聊聊string和[]byte的转换吗?

您所在的位置:网站首页 内存和字节转换的区别 面试官:你能聊聊string和[]byte的转换吗?

面试官:你能聊聊string和[]byte的转换吗?

2024-07-10 08:11| 来源: 网络整理| 查看: 265

前言

哈喽,大家好,我是asong。为什么会有今天这篇文章呢?前天在一个群里看到了一份Go语言面试的八股文,其中有一道题就是"字符串转成byte数组,会发生内存拷贝吗?";这道题挺有意思的,本质就是在问你string和[]byte的转换原理,考验你的基本功底。今天我们就来好好的探讨一下两者之间的转换方式。

byte类型

我们看一下官方对byte的定义:

// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is // used, by convention, to distinguish byte values from 8-bit unsigned // integer values. type byte = uint8

我们可以看到byte就是uint8的别名,它是用来区分字节值和8位无符号整数值。

其实可以把byte当作一个ASCII码的一个字符。

示例:

var ch byte = 65 var ch byte = '\x41' var ch byte = 'A' []byte类型

[]byte就是一个byte类型的切片,切片本质也是一个结构体,定义如下:

// src/runtime/slice.go type slice struct {     array unsafe.Pointer     len   int     cap   int }

这里简单说明一下这几个字段,array代表底层数组的指针,len代表切片长度,cap代表容量。看一个简单示例:

func main()  {  sl := make([]byte,0,2)  sl = append(sl, 'A')  sl = append(sl,'B')  fmt.Println(sl) }

根据这个例子我们可以画一个图:

string类型

先来看一下string的官方定义:

// string is the set of all strings of 8-bit bytes, conventionally but not // necessarily representing UTF-8-encoded text. A string may be empty, but // not nil. Values of string type are immutable. type string string

string是一个8位字节的集合,通常但不一定代表UTF-8编码的文本。string可以为空,但是不能为nil。string的值是不能改变的。

看一个简单的例子:

func main()  {  str := "asong"  fmt.Println(str) }

string类型本质也是一个结构体,定义如下:

type stringStruct struct {     str unsafe.Pointer     len int }

stringStruct和slice还是很相似的,str指针指向的是某个数组的首地址,len代表的就是数组长度。怎么和slice这么相似,底层指向的也是数组,是什么数组呢?我们看看他在实例化时调用的方法:

//go:nosplit func gostringnocopy(str *byte) string {  ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)}  s := *(*string)(unsafe.Pointer(&ss))  return s }

入参是一个byte类型的指针,从这我们可以看出string类型底层是一个byte类型的数组,所以我们可以画出这样一个图片:

string和[]byte有什么区别

上面我们一起分析了string类型,其实他底层本质就是一个byte类型的数组,那么问题就来了,string类型为什么还要在数组的基础上再进行一次封装呢?

这是因为在Go语言中string类型被设计为不可变的,不仅是在Go语言,其他语言中string类型也是被设计为不可变的,这样的好处就是:在并发场景下,我们可以在不加锁的控制下,多次使用同一字符串,在保证高效共享的情况下而不用担心安全问题。

string类型虽然是不能更改的,但是可以被替换,因为stringStruct中的str指针是可以改变的,只是指针指向的内容是不可以改变的。看个例子:

func main()  {  str := "song"  fmt.Printf("%p\n",[]byte(str))  str = "asong"  fmt.Printf("%p\n",[]byte(str)) } // 运行结果 0xc00001a090 0xc00001a098

我们可以看出来,指针指向的位置发生了变化,也就说每一个更改字符串,就需要重新分配一次内存,之前分配的空间会被gc回收。

string和[]byte标准转换

Go语言中提供了标准方式对string和[]byte进行转换,先看一个例子:

func main()  {  str := "asong"  by := []byte(str)  str1 := string(by)  fmt.Println(str1) }

标准转换用起来还是比较简单的,那你知道他们内部是怎样实现转换的吗?我们来分析一下:

string类型转换到[]byte类型

我们对上面的代码执行如下指令go tool compile -N -l -S ./string_to_byte/string.go,可以看到调用的是runtime.stringtoslicebyte:

// runtime/string.go go 1.15.7 const tmpStringBufSize = 32 type tmpBuf [tmpStringBufSize]byte func stringtoslicebyte(buf *tmpBuf, s string) []byte {  var b []byte  if buf != nil && len(s) 


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3