【Golang 面试 - 基础题】每日 5 题(五)

✍个人博客:Pandaconda-CSDN博客
📣专栏地址:http://t.csdnimg.cn/UWz06

📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪

 21. Go array 和 slice 的区别?

 1. 数组长度不同

  • 数组初始化必须指定长度,并且长度就是固定的。
  • 切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

 2. 函数传参不同

  • 数组是值类型,将一个数组赋值给另一个数组时,传递的是一份深拷贝,函数传参操作都会复制整个数组数据,会占用额外的内存,函数内对数组元素值的修改,不会修改原数组内容。

  • 切片是引用类型,将一个切片赋值给另一个切片时,传递的是一份浅拷贝,函数传参操作不会拷贝整个切片,只会复制 len 和 cap,底层共用同一个数组,不会占用额外的内存,函数内对数组元素值的修改,会修改原数组内容。

3. 计 算数组长度方式不同

  • 数组需要遍历计算数组长度,时间复杂度为 O(n)。

  • 切片底层包含 len 字段,可以通过 len() 计算切片长度,时间复杂度为 O(1)。

 22. Golang Slice 的底层实现

在 Go 语言中,Slice 是一种基于数组的数据结构,它是一个拥有指向底层数组的指针、长度和容量属性的结构体。Slice 的底层实现是一个动态数组,也就是说,它可以动态地增长和缩小,同时具有数组的许多优点,例如可以进行索引和迭代操作等。

当创建一个 Slice 时,Go 语言会在内存中分配一块连续的内存空间用于存储数据,并返回一个指向该内存区域的指针。该指针称为 Slice 的底层指针。Slice 还会记录该内存区域的长度和容量。

在 Slice 容量不足以存储新的元素时,Go 语言会自动重新分配一块更大的内存区域,并将原有的元素复制到新的内存区域中,然后将新的元素添加到新的内存区域中。这种自动扩容机制使得 Slice 的大小可以根据需要自动调整,无需手动进行内存分配和释放操作,同时也保证了 Slice 的连续性,使得它在访问和遍历元素时具有更好的性能表现。

需要注意的是,由于 Slice 是对底层数组的引用,因此多个 Slice 可以共享同一个底层数组。这种特性使得 Slice 在函数之间传递时非常高效,同时也需要注意避免对 Slice 中的元素进行修改,从而影响到其他共享同一个底层数组的 Slice。

23. G olang Slice 的扩容机制,有什么注意点?

Go 语言中的切片(Slice)具有动态扩容的能力。当切片容量不足以容纳更多元素时,就需要扩容。切片的扩容机制是在原切片容量的基础上扩容,一般是容量的 2 倍或者 1.25 倍,具体扩容的倍数由实现算法决定。以下是关于切片扩容的一些注意点:

  1. 切片扩容会重新分配一块连续的内存空间,因此需要将原切片中的元素复制到新的内存空间中,这个过程可能会比较耗时。

  2. 在使用 append() 函数向切片添加元素时,如果添加的元素个数超出了切片的容量,那么就会触发扩容操作。

  3. 切片扩容会导致原切片和新切片指向不同的内存空间,因此原切片的修改不会影响新切片的值。

  4. 切片扩容并不是每次添加元素都会触发,而是当切片容量不足以容纳更多元素时才会触发。

  5. 当切片容量小于 1024 时,扩容时新的容量会翻倍;当容量大于等于 1024 时,新的容量会增加原来容量的 1/4,也就是乘以 1.25。

  6. 由于切片底层是基于数组实现的,因此切片扩容时,如果原数组的容量不足以容纳新的元素,也会触发数组的重新分配和拷贝。

总之,切片的扩容机制需要注意性能和内存问题,特别是在大规模数据处理中,应该尽量减少切片扩容的次数。

 24. 扩容前后的 Slice 是否相同?

在 Golang 中,扩容前后的 Slice 是不同的。在进行 Slice 扩容时,会创建一个新的底层数组,并将原来的元素拷贝到新的数组中。因此,扩容前后的 Slice 指向的底层数组是不同的。

原因

Golang 中的 Slice 是基于数组实现的,因此在创建 Slice 时,底层会创建一个数组来存储数据。当 Slice 中的元素个数超过底层数组的容量时,就需要进行扩容。而在 Golang 中,数组的大小是固定的,无法进行扩容,因此需要创建一个新的底层数组,并将原来的元素拷贝到新的数组中。这样就可以实现 Slice 的扩容了。由于扩容后底层数组的地址已经发生了变化,因此扩容前后的 Slice 底层数组是不同的,即扩容前后的 Slice 不再共享底层数组。

 25. Go slice 的底层实现原理

切片是基于数组实现的,它的底层是数组,可以理解为对底层数组的抽象

源码包中 src/runtime/slice.go 定义了 slice 的数据结构:

type slice struct {array unsafe.Pointerlen   intcap   int
}

slice 占用 24 个字节:

  • array: 指向底层数组的指针,占用 8 个字节。

  • len: 切片的长度,占用 8 个字节。

  • cap: 切片的容量,cap 总是大于等于 len 的,占用 8 个字节。

slice 有 4 种初始化方式:

// 初始化方式1:直接声明
var slice1 []int// 初始化方式2:使用字面量
slice2 := []int{1, 2, 3, 4}// 初始化方式3:使用make创建slice
slice3 := make([]int, 3, 5)         // 初始化方式4: 从切片或数组“截取”
slcie4 := arr[1:3]

通过一个简单程序,看下 slice 初始化调用的底层函数。

package mainimport "fmt"func main() {slice := make([]int, 0)slice = append(slice, 1)fmt.Println(slice, len(slice), cap(slice))
}

通过 go tool compile -S test.go | grep CALL 得到汇编代码。

0x0042 00066 (test.go:6)        CALL    runtime.makeslice(SB)
0x006d 00109 (test.go:7)        CALL    runtime.growslice(SB)
0x00a4 00164 (test.go:8)        CALL    runtime.convTslice(SB)
0x00c0 00192 (test.go:8)        CALL    runtime.convT64(SB)
0x00d8 00216 (test.go:8)        CALL    runtime.convT64(SB)
0x0166 00358 ($GOROOT/src/fmt/print.go:274)     CALL    fmt.Fprintln(SB)
0x0180 00384 (test.go:5)        CALL    runtime.morestack_noctxt(SB)
0x0079 00121 (<autogenerated>:1)        CALL    runtime.efaceeq(SB)
0x00a0 00160 (<autogenerated>:1)        CALL    runtime.morestack_noctxt(SB)

初始化 slice 调用的是 runtime.makeslice,makeslice 函数的工作主要就是计算 slice 所需内存大小,然后调用 mallocgc 进行内存的分配。

所需内存大小 = 切片中元素大小 * 切片的容量

func makeslice(et *_type, len, cap int) unsafe.Pointer {mem, overflow := math.MulUintptr(et.size, uintptr(cap))if overflow || mem > maxAlloc || len < 0 || len > cap {// NOTE: Produce a len out of range error instead of a// cap out of range error when someone does make([]T, bignumber).// cap out of range is true too, but since the cap is only being// supplied implicitly, saying len is clearer.// See golang.org/issue/4085.mem, overflow := math.MulUintptr(et.size, uintptr(len))if overflow || mem > maxAlloc || len < 0 {panicmakeslicelen()}panicmakeslicecap()}return mallocgc(mem, et, true)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/51320.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】—— 类和对象(一)

【C】—— 类和对象&#xff08;一&#xff09; 1、类的定义1.1、类定义1.1.1、类定义格式1.1.2、成员变量的标识1.1.3、C 中的 s t r u c t struct struct1.1.4、C 中的内联函数1.1.5、总结 1.2、访问限定符1.3、类域 2、实例化2.1、实例化的概念2.2、对象大小2.2.1、对象的大…

昇思MindSpore 应用学习-LSTM+CRF序列标注-CSDN

LSTMCRF序列标注 AI代码解析 本篇案例暂不支持在windows系统上运行。 概述 序列标注指给定输入序列&#xff0c;给序列中每个Token进行标注标签的过程。序列标注问题通常用于从文本中进行信息抽取&#xff0c;包括分词(Word Segmentation)、词性标注(Position Tagging)、命名实…

未来十年硬件工程师有没有前景?

未来十年&#xff0c;硬件工程师的就业前景依然保持乐观&#xff0c;并且具有多个有前景的发展方向。以下是对未来十年硬件工程师前景的详细分析&#xff1a; 一、市场需求持续增长 随着科技的快速发展&#xff0c;多个领域对硬件工程师的需求将持续增长。例如&#xff0c;物…

docker常用命令集锦

目录 一、查看版本信息 1.1 查看 Docker CLI 版本&#xff1a; 1.2 查看 Docker 详细版本信息&#xff1a; 1.3 查看 Docker 系统信息&#xff1a; 二、进入和退出容器 2.1 进入容器&#xff1a; 2.2 退出容器&#xff1a; 2.3 查看容器日志&#xff1a; 2.4 查看容器的…

什么是客户自助服务?优点和最佳实践

什么是客户自助服务&#xff1f; 客户自助服务是解决客户问题的一种方法&#xff0c;客户可以在其中找到自己的解决方案。客户可以使用自助服务门户自行研究和解决问题&#xff0c;而不是与公司或产品的客户服务代表合作。除了故障排除之外&#xff0c;自助服务还可以用于安装…

Java入门:05.Java中的数组003

在上两篇文章中&#xff0c;我们了解什么是数组类型的数据&#xff0c;并明白了怎样使用new关键字创建一个数组&#xff0c;并为其定义长度。 同时在理解了栈空间和堆空间的区别后&#xff0c;我们也知道了&#xff0c;想要使用一个数组&#xff0c;我们必须将他的引用地址赋给…

lenovo联想ThinkBook 14 G4+ IAP/ARA(21CX,21D0)笔记本原装出厂Windows11系统预装OEM镜像下载

ThinkBook 14 G4 IAP【21CX】原厂系统Win11恢复安装包&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1iY9BxidIbv4RnXKaqbydTA?pwd9wc6 提取码&#xff1a;9wc6 ThinkBook 14 G4 ARA【21D0】原厂系统Win11恢复安装包&#xff1a; 链接&#xff1a;https://pan.ba…

nest学习笔记(一)

介绍 nest是一个用于构建高效&#xff0c;可拓展的nodejs服务端应用程序的框架&#xff0c;它使用渐进式javascript&#xff0c;使用Typescript构建并且完全支持Typescript&#xff0c;而且运行开发者使用javascript编写代码&#xff0c;提供了OOP、FP、FRP nest的底层是基于…

Linux编程:使用python或者shell获取系统信息

0. 概要 在日常的系统管理和性能监控中&#xff0c;获取系统信息是一个非常常见的需求。 本文将介绍如何使用Python和Shell脚本来获取这些系统信息。 1. 使用Python获取系统信息 使用psutil库来获取系统的CPU、内存、磁盘和网络信息。 1.1 安装psutil库 首先&#xff0c;我…

积分环节1/s

在控制工程中&#xff0c;当我们处理动态系统模型时&#xff0c;积分环节常常表示为1/s&#xff0c;这里的"1"代表系统的增益&#xff0c;而"s"是一个复数频率变量&#xff0c;通常在拉普拉斯变换中使用&#xff0c;它代表了时间域中的单位阶跃响应。积分运…

盗梦空间续集(InceptionNeXt):使用Inception优化加速ConvNeXt实现ImageNet-1K的最佳精度

Abstract 灵感来自ViT的长距离建模能力&#xff0c;大核卷积最近被广泛研究和采用&#xff0c;以扩大感受野并提高模型性能&#xff0c;例如显著的工作ConvNeXt采用77深度卷积。虽然这种深度算子只消耗少量的FLOPs&#xff0c;但由于高内存访问成本&#xff0c;它在强大计算设…

JavaScript 进阶

JavaScript 进阶 在掌握了 JavaScript 的基础知识之后&#xff0c;继续深入学习其高级特性和应用技巧将有助于编写更高效、更优雅的代码。本文将详细介绍 JavaScript 的一些进阶概念和技术。 目录 闭包原型与继承高级函数 高阶函数柯里化函数组合 异步编程 Promiseasync/awa…

MySQL InnoDB的读写参数优化

MySQL InnoDB的读写参数优化是提升数据库性能的重要手段。以下是对MySQL InnoDB读写参数优化的详细阐述&#xff1a; 一、读参数优化 缓冲池大小&#xff08;innodb_buffer_pool_size&#xff09; 作用&#xff1a;该参数决定了InnoDB存储引擎可以使用的内存大小&#xff0c;…

PHP设计模式-简单工厂模式

核心&#xff1a; 一、定义一个接口类里面写规定好的方法。 interface Message{public function send(array $params);public function getMessage(array $params);public function getCode(array $params);} 二、定义产品类 、产品类继承接口类 class AlliYunSms implements …

Dart sprintf包使用指南:格式化输出的瑞士军刀

Dart sprintf包使用指南&#xff1a;格式化输出的瑞士军刀 简介 sprintf 是Dart语言的一个扩展包&#xff0c;提供了类似于C语言中 sprintf 函数的功能。它允许你格式化字符串&#xff0c;支持占位符和各种格式化选项&#xff0c;非常适合于创建具有特定格式的日志、用户界面…

java实战项目--拼图小游戏(附带全套源代码)

个人主页VON 所属专栏java实战项目游戏参考黑马程序员 一、效果展示 二、功能介绍 游戏中所有的图片以及代码均已打包&#xff0c;玩家直接安装游戏即可&#xff0c;不用idea也可以畅玩。 游戏功能比较单一&#xff0c;只有简单的拼图功能。 a&#xff1a;展示原图重新游戏&a…

《汇编语言 基于x86处理器》- 读书笔记 - 第3章-汇编语言基础

《汇编语言 基于x86处理器》- 读书笔记 - 第3章-汇编语言基础 3.1 基本语言元素3.1.1 第一个汇编语言程序常见汇编语言调用规范 3.1.2 整数常量&#xff08;基数、字面量&#xff09;3.1.3 整型常量表达式3.1.4 实数常量十进制实数十六进制实数&#xff08;编码实数&#xff09…

Netty技术全解析:SimpleChannelInboundHandler详解

Netty是一个高性能、异步事件驱动的NIO框架&#xff0c;它提供了对TCP、UDP和文件传输的支持&#xff0c;并且能够简化网络应用程序的开发过程。在Netty中&#xff0c;ChannelInboundHandler接口用于处理入站事件&#xff0c;即外部数据或操作进入Netty应用程序时的事件。而Sim…

USB 2.0 协议专栏之 USB 2.0 概述(一)

前言&#xff1a;本篇博客为手把手教学的 USB 2.0 协议栈类精品博客&#xff0c;该专栏博客侧重针对 USB 2.0 协议进行讲解。Universal Serial Bus 作为如今最常见的通信接口&#xff0c;被广泛应用于&#xff1a;Keyboard、Mouse、Communication Device Class 和 Mass Storage…

Linux操作系统 -socket网络通信

同一台主机之间的进程 1.古老的通信方式 无名管道 有名管道 信号 2、IPC对象通信 system v 消息队列 共享内存 信号量集 由于不同主机间进程通信 3.socket网络通信 国际网络体系结构&#xff1a; 七层OSI模型(理论…