您的位置:首页 > 财经 > 产业 > 微信开放平台表情包_合肥政务服务网_汕头百度关键词推广_竞价点击软件排名

微信开放平台表情包_合肥政务服务网_汕头百度关键词推广_竞价点击软件排名

2025/1/5 18:12:36 来源:https://blog.csdn.net/realize_dream/article/details/144309651  浏览:    关键词:微信开放平台表情包_合肥政务服务网_汕头百度关键词推广_竞价点击软件排名
微信开放平台表情包_合肥政务服务网_汕头百度关键词推广_竞价点击软件排名

1、什么变量大小是 0 字节

如何看变量在内存占了多少个字节,unsafe 包下的 Sizeof 可以查看。

基本类型的字节数:

func main() {  fmt.Println(unsafe.Sizeof(int(0)))   // 8 跟着系统的,如果32位,则占4个字节  fmt.Println(unsafe.Sizeof(int32(0))) // 4 如果int32,则永远占4个字节  fmt.Println(unsafe.Sizeof(int64(0))) // 4 如果int64,则永远占8个字节  i := int(0)  p := &i  fmt.Println(unsafe.Sizeof(p)) // 结果:8 也是跟随系统字长的  
}

int 大小跟随系统字长,指针大小也是一样

空结构体

空结构体的地址均相同,空结构体占 0 个字节。

type K struct {}  func main() {  fmt.Println(unsafe.Sizeof(K{})) // 结果:0  
}

空结构体可以节约内存,使用场景:chan 发信号通知、 map 只要 key 不要 value

如使用 Map 的时候,只要 key 不要 value ,那么 空 struct{} 可以作为 value ,可以节约内存。

func main() {  // 场景1:只要 key ,不要 value   m := map[string]struct{}{}  m["Alin"] = struct{}{}  // 场景2:chan 在创建 chan 时候,后续需要跟上负载,如果只想发个信号,int 占8个字节,bool 占 1个字节  ch := make(chan struct{})  fmt.Println(ch)  
}

2、数组、字符串、切片底层是一样的吗?

String 底层

字符串本质是个结构体,是个变长编码

如果对字符串直接使用下标访问,得到的是字节,所以需要 range 遍历时,被解码成 rune 类型的字符。

func main() {  str := "哈喽hello"  fmt.Println(unsafe.Sizeof(str))  fmt.Println(len(str))          // 11 字节for _, char := range str {  fmt.Println(char)              // 得到字节,转string
}
}// string 底层是个 struct 结构体
type stringStruct struct {  str unsafe.Pointer    // 指针指向底层 Byte 数组len int               // 表示 Byte 数组的长度(字节数)
}

切片(Slice)

切片底层源码:

// runtime/slice.go
type slice struct {  array unsafe.Pointer  // 切片其实就是对数组的引用len   int             // 这个长度,是引用的部分的长度cap   int  
}

切片的创建:

// 1. 根据数组创建
arr[0:3] or slice[0:3]// 2. 字面量:编译时插入创建数组的代码
// 字面量方式,底层也是先创建一个数组 go build -o -gcflags -S main.goslice := []int{1,2,3}
// 3. make:运行时创建数组
// 底层调用makeslice函数
slice := make([]int,3)

切片追加(append)

添加如果不扩容,只调整 len 长度即可。

若需要扩容,会去调用 runtime.growslice() 进行扩容,当切片长度小于 256 将会翻倍扩容,大于 256 每次增加 25%,切片扩容时,并发不安全,需要并发加锁。

// go1.19
func growslice(et *_type, old slice, cap int) slice {  newcap := old.cap  doublecap := newcap + newcap  if cap > doublecap {  newcap = cap  } else {  const threshold = 256  if old.cap < threshold {  newcap = doublecap  } else {  // Check 0 < newcap to detect overflow  // and prevent an infinite loop.         for 0 < newcap && newcap < cap {  // Transition from growing 2x for small slices  // to growing 1.25x for large slices. This formula            // gives a smooth-ish transition between the two.            newcap += (newcap + 3*threshold) / 4  }  // Set newcap to the requested cap when  // the newcap calculation overflowed.         if newcap <= 0 {  newcap = cap  }  }  }  ......return slice{p, old.len, newcap}  
}

slice 是深拷贝还是浅拷贝?

(1)浅拷贝:

如何直接赋值的话,是浅拷贝的,修改新的接收这个变量是会影响原来的数组

func main() {  slice := []int{1, 2}  newSlice := slice  newSlice[0] = 3  fmt.Println("slice:", slice)  fmt.Println("newSlice:", newSlice) fmt.Printf("%p, %p\n", slice, newSlice)    
}结果:
slice: [3 2]
newSlice: [3 2]
0xc0000200b0, 0xc0000200b0

(2)深拷贝:

func main() {  slice := []int{1, 2}  fmt.Println("slice: ", slice)  fmt.Printf("%p\n", slice)  slice = append(slice, 8, 9)  slice[0] = 100  fmt.Println("slice: ", slice)  fmt.Printf("%p\n", slice)  
}

在 Go 中,切片、 map 、chan 都是是引用传递

当你将一个切片作为参数传递给一个函数时,函数中的任何更改都会反映在原始切片中。这是因为切片本身只是一个指向底层数组的指针、长度和容量的结构体,而不是实际的数据。因此,当你传递一个切片时,函数接收到的是指向相同底层数组的指针,因此它可以修改原始切片中的数据

3、Map

3.1、哈希冲突解决方案

开放寻址法、拉链法

底层数据结构:

// A header for a Go map.  
type hmap struct {  count     int // 统计k,v数量flags     uint8  B         uint8 // 桶数量的对数 log_2noverflow uint16 hash0     uint32 // 哈希种子buckets  unsafe.Pointer //桶说明拉链法,把相同hash值k,v放一个桶,避免碰撞问题oldbuckets unsafe.Pointer nevacuate  uintptrextra *mapextra
}type mapextra struct {    overflow     *[]*bmap  oldoverflow  *[]*bmap  nextOverflow *bmap  
}// A bucket for a Go map.  
type bmap struct {   tophash [bucketCnt]uint8   
}

4、接口

5、nil、空接口、空结构体有什么区别?

// Type 可能是pointer, channel, func, interface, map, or slice type
var nil Type

nil

nil 是空,并不一定是 ”空指针“,nil 是 6 种类型的零值。每种类型的 nil 是不同的,无法比较。

空结构体

空结构体的值不是 nil ,空结构体的指针也不是 nil,但是都相同(zerobase)

空接口

空接口零值是 nil,一旦有类型信息就不是 nil。

var a interface{}  
fmt.Println(a == nil) // true  var b *int  
a = b  
fmt.Println(a == nil) // false

6、内存对齐

S1 和 S2 在内存占用多大内存

type S1 struct {  num1 int32  num2 int32  
}  type S2 struct {  num1 int16  num2 int32  
}  func main() {  fmt.Println(unsafe.Sizeof(S1{})) // 8  fmt.Println(unsafe.Sizeof(S2{})) // 8  
}

非内存对齐:内存的原子性与效率受到影响

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com