10.Go常识
# Go常识
# 01.字符串底层
# 1、 字符串底层结构
Go中,
string
是一种只读的字节序列
底层由一个结构体实现
的,主要包含以下两个部分指针
:指向存储字符串内容的连续内存块
,实际存储的内容是字节序列
([]byte
)长度
:len
用来表示字符串的长度(字符串不可变,底层字节序列不会被修改)
当容量不够时会
重新申请新的内存
,Data 指针将指向新的地址
,原来的地址空间将被释放
type StringHeader struct {
Data uintptr // 字符串首地址,指向底层字节数组的指针
Len int // 字符串长度
}
1
2
3
4
2
3
4
- 对于字符串Hello,实际底层结构如下
# 2、reflect.StringHeader
- 字符串和数组类似,内置的 len 函数返回字符串的长度
- 也可以通过 reflect.StringHeader 结构访问字符串的长度
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
a :="aaa"
// 1.unsafe.Pointer(&a)方法可以得到变量a的地址
fmt.Println(unsafe.Pointer(&a)) // 0xc000054240
// 2.(*reflect.StringHeader)(unsafe.Pointer(&a)) 可以把字符串a转成底层结构的形式
b := (*reflect.StringHeader)(unsafe.Pointer(&a)).Len
fmt.Println(b) // 3
// 3.(*[]byte)(unsafe.Pointer(&ssh)) 可以把ssh底层结构体转成byte的切片的指针
// 4.再通过 *转为指针指向的实际内容
ssh := *(*reflect.StringHeader)(unsafe.Pointer(&a))
c := *(*[]byte)(unsafe.Pointer(&ssh))
fmt.Printf("%v",c) // [97 97 97]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 02.数组
数组
数组是一个具有
固定长度
的同类型元素的集合
,不能改变如果需要存储更多的元素,就需要先创建一个更长的数组,再把原来数组里的值复制到新数组里
切片
切片是一个基于数组的动态数据结构,其长度和容量可以动态变化
切片底层内存也是在连续块中分配的
切片本质上是一个结构体,包含了以下三个部分
- 指针:
指向存储数据的数组
- 长度:
元素个数
- 容量:从切片开始位置到底层数组末尾的元素个数,可以通过
cap()
函数获取
- 指针:
# 03.pointer
指针是一种变量,其值是另一个变量的地址
- 通过指针,我们可以间接访问或修改该变量
- 只需要记住以下几点:
&变量名
: 获取变量的内存地址*pointor
:通过指针类型的变量,获取该指针指向的值
package main
import "fmt"
func main() {
name := "张三"
p1 := &name // &变量名: 获取变量的内存地址
p2 := *&name // *pointor:通过指针类型的变量,获取该指针指向的值
fmt.Println(name,p1,p2)
// 张三 0xc000088230 张三
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
指针作用
1)函数参数传递
- 如果你希望在函数内部修改传入的变量,就需要使用指针传递变量的地址,而不是传递副本
2)优化内存分配
- 通过指针可以避免不必要的内存拷贝,尤其是在传递大型结构体时
- 传递指针只会传递地址,而不是整个结构体的副本
- 通过指针可以避免不必要的内存拷贝,尤其是在传递大型结构体时
# 04.interface
- golang中的接口分为
带方法的接口和空接口
iface
:表示带方法的接口eface
:表示空接口
在 Go 中,
interface
可以理解为一组方法的集合- 当一个类型实现了某个
interface
的所有方法时 - 这个类型就实现了这个
interface
,并且可以被赋值给该interface
变量
- 当一个类型实现了某个
interface{}
是 Go 的空接口,表示任意类型
# 1、eface空接口
type
: 指向值的具体类型信息(type
descriptor)data
: 存储具体的值(value
)
type emptyInterface struct {
typ *type // 指向值类型的指针
data unsafe.Pointer // 存储值的指针
}
1
2
3
4
2
3
4
# 2、iface带方法的接口
- 对于非空接口(定义了方法的接口),它的内部表示更加复杂,包含三部分
itab
: 接口表,存储该接口的具体实现信息
(包含两个重要的部分)inter
: 指向接口类型信息
type
: 指向具体实现该接口的类型信息
data
: 存储具体的值
methods
: 接口的方法集合
type iface struct {
tab *itab // 指向接口表的指针
data unsafe.Pointer // 存储值的指针
}
1
2
3
4
2
3
4
# 05.make和new
new 和 make 是两个内置函数,主要用来创建并分配类型的内存。
make和new区别
make
关键字的作用是创建于slice、map 和 channel 等内置的数据结构
new
的作用是为类型申请一片内存空间,并返回指向这片内存的指针
func main() {
a := make([]int, 3, 10) // 切片长度为 1,预留空间长度为 10
a = append(a,1)
fmt.Printf("%v--%T \n",a,a) // [0 0 0]--[]int 值----切片本身
var b = new([]int)
//b = b.append(b,2) // 返回的是内存指针,所以不能直接 append
*b = append(*b, 3) // 必须通过 * 指针取值,才能进行 append 添加
fmt.Printf("%v--%T",b,b) // &[]--*[]string 内存的指针---内存指针
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 06.深浅拷贝
- 浅拷贝就是
只拷贝指针的值
,指针指向的内容只有一份
而深拷贝是把指针指向的值拷贝一份
- slice的
浅拷贝
就是指slice变量赋值操作
- slice的
深拷贝
就是指使用内置的copy函数来拷贝两个slice
package main
import "fmt"
func main() {
SliceShallowCopy()
SliceDeepCopy()
}
func SliceShallowCopy() {
src := []byte {1,2,3,4,5,6}
dst := src
fmt.Println("浅拷贝原始数据",src) // [1 2 3 4 5 6]
dst[0]=10
// 修改拷贝数据,原始数据会以前跟着改变
fmt.Println("after modify[src]:",src) // [10 2 3 4 5 6]
}
func SliceDeepCopy() {
src := []byte {1,2,3,4,5,6}
var dst = make([]byte, len(src))
copy(dst[:], src)
fmt.Println("深拷贝前:",src) // [1 2 3 4 5 6]
dst[0]=10
fmt.Println("深拷贝修改拷贝数据值后:",src) // [1 2 3 4 5 6]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
上次更新: 2025/2/25 11:09:45