Golang基础常识性知识面试中常见的六大陷阱及应对技巧

news/2024/9/20 7:36:11/ 标签: golang, 开发语言, 后端

一、nil slice & empty slice

1、nil切片与空切片底层

  • nil切片:var nilSlice [] string

    • nil slice的长度len和容量cap都是0

    • nil slice==nil

    • nil slice的pointer是nil

  • 空切片:emptySlice0 := make([]int,0)

    • empty slice的长度是0,容量是由指向底层数组决定

    • empty slice != nil

    • empty slice的pointer是底层数组的地址

  • nil切片和空切片最大的区别在指向的数组引用地址是不一样的

  • nil空切片引用数组指针地址为0(无指向任何实际地址)

图片

  • 空切片的引用数组指针地址是有的,且固定为一个值,所有的空切片指向的数组引用地址都是一样的

图片

2、创建nil slice 和empty slice

package mainimport "fmt"
func main() {  var nilSlice []string  // 创建一个 nil 切片  emptySlice0 := make([]int, 0)  // 方法1:创建一个空切片(零切片)  var emptySlice1 = []string{}   // 方法2:创建一个空切片
  fmt.Printf("\nnilSlice---> Nil:%v Len:%d Capacity:%d", nilSlice == nil, len(nilSlice), cap(nilSlice))  fmt.Printf("\nemptySlice0---> nil:%v Len:%d Capacity:%d", emptySlice0 == nil, len(emptySlice0), cap(emptySlice0))  fmt.Printf("\nemptySlice1---> nil:%v Len:%d Capacity:%d", emptySlice1 == nil, len(emptySlice1), cap(emptySlice1))
  // nil切片和空切片都可以正常 append数据  nilSlice = append(nilSlice, "sss")}/*Nil:true Len:0 Capacity:0nil:false Len:0 Capacity:0nil:false Len:0 Capacity:0[sss] */

二、类型强转产生内存拷贝

1、字符串转数组发送内存拷贝

  • 字符串转成byte数组,会发生内存拷贝吗?

  • 字符串转出切片,会产生拷贝

  • 严格来说,只要是发送类型强转都会发送内存拷贝

  • 那么问题来了,频繁的内存拷贝操作听起来对性能不大友好

  • 有没有什么办法可以在字符串转出切片的时候不用发生拷贝呢?

2、字符串转数组不内存拷贝方法

  • 那么如果想要在底层转换二者,只需要吧StringHeader的地址强转成SliceHeader就行,那么go有个很强的包叫unsafe

  • 1.unsafe.Pointer(&a)方法可以得到变量a的地址。

    • 2.(*reflect.StringHeader)(unsafe.Pointer(&a)) 可以把字符串a转成底层结构的形式。

    • 3.(*[]byte)(unsafe.Pointer(&ssh)) 可以把ssh底层结构体转成byte的切片的指针。

    • 4.再通过 *转为指针指向的实际内容。

package main
import (   "fmt"   "reflect"   "unsafe")
func main() {   a :="aaa"   ssh := *(*reflect.StringHeader)(unsafe.Pointer(&a))   b := *(*[]byte)(unsafe.Pointer(&ssh))   fmt.Printf("%v---%T",b,b)  // [97 97 97]---[]uint8}

三、拷贝大切片一定代价大吗?

  • SliceHeader 是切片在go的底层结构。

    • 第一个字是指向切片底层数组的指针,这是切片的存储空间

    • 第二个字段是切片的长度

    • 第三个字段是容量

type SliceHeader struct {  Data uintptr  Len  int  Cap  int}
  • 大切片跟小切片的区别无非就是 Len 和 Cap的值比小切片的这两个值大一些,如果发生拷贝,本质上就是拷贝上面的三个字段。

  • 所以 拷贝大切片跟小切片的代价应该是一样的

四、map不初始化使用会怎么样

  • 空map和nil map结果是一样的,都为map[]。

  • 所以,这个时候别断定map是空还是nil,而应该通过map == nil来判断。

package main
func main() {  var m1 map[string]string    // 创建一个 nil map  println("m1为nil: ", m1==nil)  // 报错 => panic: assignment to entry in nil map  //m1["name"] = "tom"
  var m2 =  make(map[string]string)  // 创建一个空map  m2["name"] = "jack"                // 空map可以正常  println("m2为nil: ", m2==nil)}

五、map会遍历删除安全吗?

  • map 并不是一个线程安全的数据结构。

  • 同时读写一个 map 是未定义的行为,如果被检测到,会直接 panic。

  • 上面说的是发生在多个协程同时读写同一个 map 的情况下。

  • 如果在同一个协程内边遍历边删除,并不会检测到同时读写,理论上是可以这样做的。

  • sync.Map可以解决多线程读写map问题

    • 一般而言,这可以通过读写锁来解决:sync.RWMutex

    • 读之前调用 RLock() 函数,读完之后调用 RUnlock() 函数解锁;

    • 写之前调用 Lock() 函数,写完之后,调用 Unlock() 解锁。

    • 另外,sync.Map 是线程安全的 map,也可以使用

六、for循环append坑

1、坑1:添加元素变覆盖

  • 不会死循环,for range其实是golang语法糖,在循环开始前会获取切片的长度 len(切片),然后再执行len(切片)次数的循环。

package mainimport "fmt"func main() {  s := []int{1,2,3,4,5}  for _, v:=range s {    s =append(s, v)    fmt.Printf("len(s)=%v\n",len(s))  }}/*len(s)=6len(s)=7len(s)=8len(s)=9len(s)=10 */

2、坑2:值全部一样

  • 每次循转中num的值是正常的,但是由append构造的res中,全是nums的最后一个值。

  • 最终总结出原因是在for range语句中,创建了变量num且只被创建了一次。

  • 即num有自己的空间内存且地址在for循环过程中不变

  • 循环过程中每次将nums中对应的值和num进行值传递

package mainimport "fmt"func main() {  var nums = []int{1, 2, 3, 4, 5}  var res []*int  for _, num := range nums {    res = append(res, &num)    //fmt.Println("num:", num)  }  for _, r := range res {    fmt.Println("res:", *r)  }}/*res: 5res: 5res: 5res: 5res: 5 */

3、解决方法

  • 方法1

    • 不使用for range的形式,直接用索引来对nums取值

package mainimport "fmt"func main() {  var nums = []int{1, 2, 3, 4, 5}  var res []*int  for i := 0; i < len(nums); i++ {    res = append(res, &nums[i])  }  fmt.Println("res:", res)  for _, r := range res {    fmt.Println("res:", *r)  }}
  • 方法2

    • 在for循环中每次再定义一个新的变量num_temp,将num的值传给num_temp,之后append该变量即可。

package mainimport "fmt"func main() {  var nums = []int{1, 2, 3, 4, 5}  var res []*int  for _, num := range nums {    numTemp := num // 创建一个新的临时变量    res = append(res, &numTemp)  }  for _, r := range res {    fmt.Println("res:", *r)  }}/*res: 1res: 2res: 3res: 4res: 5 */

http://www.ppmy.cn/news/1504236.html

相关文章

信息安全工程师题

《中华人民共和国网络安全法》是为保障网络安全&#xff0c;维护网络空间主权和国家安全、社会公共利益&#xff0c;保护公民、法人和其他组织的合法权益&#xff0c;促进经济社会信息化健康发展制定。由全国人民代表大会常务委员会于2016年11月7日发布&#xff0c;自2017年6月…

Spring Boot项目实现调用腾讯云进行人脸识别

​ 博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 背景 随着科技的不断发展&#xff0c;人脸识别技术已经成为了一种日益重要的身份验证和安全监控手段。腾讯云人脸识别功能便是其中之一&#xff0c;而Java语言又是现在最为常用且高效的编程语言之一。本篇论…

数据结构之多维数组的存储

一、多维数组的定义 多维数组是由n&#xff08;n≥1&#xff09;个相同数据类型的数据元素组成的有限序列&#xff0c;其中每个元素本身也可以是一个数组。 最常见的多维数组是二维数组&#xff0c;可以看作是一维数组的一维数组。类似地&#xff0c;三维数组可以看作是存储二…

C语言实现 -- 单链表

C语言实现 -- 单链表 1.顺序表经典算法1.1 移除元素1.2 合并两个有序数组 2.顺序表的问题及思考3.链表3.1 链表的概念及结构3.2 单链表的实现 4.链表的分类 讲链表之前&#xff0c;我们先看两个顺序表经典算法。 1.顺序表经典算法 1.1 移除元素 经典算法OJ题1&#xff1a;移除…

RabbitMQ 生产和消息

1. 安装RabbitMQ 编写docker-compose.yml文件 version: 3.8 services:rabbitmq:image: rabbitmq:managementcontainer_name: rabbitmq_containerports:- "5672:5672"- "15672:15672"volumes:- ./rabbitmq_data:/var/lib/rabbitmqenvironment:RABBITMQ_DE…

速度规划之:起点速度和终点速度不为零的非对称梯形速度规划

起点速度和终点速度不为零的非对称梯形速度规划 一、引言二、理论基础1. 梯形速度规划概述2.数学建模- 变量定义- 约束关系- 公式推导 三、计算过程1.只存在减速段2.只存在加速段3.存在加速段和减速段4.存在加速度段、匀速段和减速段 四、仿真实现五、优缺点优点缺点 六、总结 …

万顺汽车租赁推荐系统

1 项目介绍 1.1 摘要 随着汽车租赁市场的不断发展&#xff0c;为了提升用户体验和管理效率&#xff0c;开发了一套集管理员和用户功能于一体的汽车租赁平台系统。该系统旨在提供便捷的用户信息管理、车辆信息管理、租赁订单管理等功能&#xff0c;以满足管理员和用户的不同需…

CLIP论文详解

文章目录 前言一、CLIP理论1.CLIP思想2.模型结构 二、CLIP预训练1.数据集2.训练策略3.模型选择 三、Zero-Shot推理四、CLIP伪代码实现五、CLIP局限性总结 前言 CLIP这篇论文是OpenAI团队在2021年2月底提出的一篇论文&#xff0c;名字叫做《Learning Transferable Visual Models…

5.1-软件工程基础知识-软件工程概述

软件工程诞生原因 了解 早期的软件&#xff1a;主要是指采用个体工作方式实现的程序。第一次软件危机&#xff1a;20世纪60年代中期 典型表现有软件质量低下、项目无法如期完成、项目严重超支等因为软件而导致的重大事故时有发生。 软件工程的诞生&#xff1a;1968年在NATO会…

filebeat + logstash使用笔记

背景 本文中有2台主机&#xff1a; &#xff08;1&#xff09;1.1.1.1是OpenStack的nova节点&#xff0c;安装filebeat &#xff08;2&#xff09;1.1.1.2是logstash节点 在1.1.1.1上通过filebeat读取OpenStack的nova-compute组件日志&#xff08;/var/log/nova/nova-compute.…

每天一个数据分析题(四百六十七)- 因子分析

因子分析中因子旋转的方法有&#xff08;&#xff09; A. 方差最小正交旋转 B. 方差最大正交旋转 C. 斜交旋转 D. 相交旋转 数据分析认证考试介绍&#xff1a;点击进入 题目来源于CDA模拟题库 点击此处获取答案 数据分析专项练习题库 内容涵盖Python&#xff0c;SQL&a…

uniapp打开地图直接获取位置

uniapp官网文档 https://en.uniapp.dcloud.io/api/location/open-location.html <view class"map-content" click.stop"kilometer(item)"><view class"km">{{item.distance||0}}km</view></view>import map from ../../…

【Python特征工程系列】数值型数据缺失值处理方法总结(案例+源码)

这是我的第334篇原创文章。 一、引言 对于从事数据相关工作的小伙伴&#xff0c;面试的时候经常会被问到如何进行缺失值/异常值的处理&#xff0c;本文来梳理一下填补数值型缺失值的7种方法。 二、实现过程 准备数据 df pd.read_csv(data.csv) df.drop("id",axis…

fastDDS-gen编译

要编译 fastDDS-gen&#xff0c;你可以按照以下步骤进行&#xff1a; 克隆仓库&#xff1a; git clone --recursive https://github.com/eProsima/Fast-DDS-Gen.git cd Fast-DDS-Gen构建和安装&#xff1a; fastDDS-gen 使用 Gradle 进行构建。你可以使用以下命令来构建和安装&…

MSF回弹木马ubuntu测试

网站地址为192.168.104.128 web.php内容为&#xff1a; <?php eval($_POST[123]); ?>linux版本信息&#xff1a;20.04.1-Ubuntu nginx信息&#xff1a;nginx-1.21.6 php信息&#xff1a;PHP 7.3.33-19 php-fpm信息&#xff1a;/etc/php/7.3/fpm/php-fpm.conf 一、使用…

AUTOSAR实战教程 - 模式管理BswM与其他各模块的交互

近日驻厂某OEM,幸得大块的个人时间, 把BswM这一块的内容从ETAS/ISOLAR工具配置到代码实现做了一个全方位的CT. 2024,希望孜孜内卷的汽车人升职加薪!博主近期写的一首小诗,也一并送给大家,懂的都懂: 在看不到阳光的冬天/ 我染了风寒/ 白天点灯/ 晚上吃药/ 躺在被窝里才敢咳…

用的到linux-tomcat端口占用排查-Day5

前言&#xff1a; 最近使用tomcat搭建了一套测试环境的应用&#xff0c;整个搭建过程也很简单&#xff0c;就是将部署包上传至服务器☞解压☞启动tomcat服务器&#xff0c;当然服务器也是成功启动了&#xff0c;但是发现前端应用报404&#xff0c;具体如下图所示。 一、现象及思…

【Linux-WMware Tools安装失败“segmentation fault”解决方法】

VMware版本&#xff1a;17 Ubuntu版本: 22.04 安装常规办法&#xff0c;通过vmware安装Tool&#xff0c;安装显示报错&#xff1a;“segmentation fault”&#xff0c;查了下可能是tool和ubuntu版本不兼容导致的。解决办法&#xff1a;通过命令行逐次安装。 1、sudo apt insta…

nginx负载均衡、java、tomcat装包

一、nginx 七层负载均衡 1、七层负载均衡基础配置 2、负载均衡状态 [rootserver]# vim /usr/local/nginx/conf/nginx.confworker_processes 1;event {worker_connections 1024&#xff1b;}http { # 七层负载均衡支持http、ftp协议include mime.types;default_type app…

手把手教你入门vue+springboot开发(十一)--token详细解读

文章目录 前言一、后端代码解读二、前端代码解读总结前言 JWT是一种在web应用中广泛使用的令牌格式,用于在用户和服务器之间传递安全可靠的信息。JWT通常包含了用户的身份信息和一些其他的元数据,被用作身份验证和授权。因此,人们经常将JWT简称为令牌(token)。本篇我们结…