转载请注明出处:

  在Go语言中,切片(Slice)和指针的切片(即切片中每个元素都是指向某种数据类型的指针)是两个不同的概念,它们各自具有特定的用途和优势。

切片(Slice)

  切片是对数组的一个连续片段的引用,它提供了对数组元素集合的抽象表示。切片底层数据结构都是数组,它包含三个关键部分:指向数组起始元素的指针、切片长度和切片容量。切片长度是指切片当前包含的元素个数,而切片容量是指从切片的起始元素到底层数组的最后一个元素的个数。

  切片的一个重要特性是它提供了对底层数组的动态视图。这意味着你可以通过切片来访问、修改和操作数组的一部分或全部元素,而无需复制整个数组。此外,切片还可以方便地进行扩容和缩容操作,以满足不同场景下的需求。

  示例

package main  

import "fmt"  

func main() {
// 定义一个数组
array := [5]int{1, 2, 3, 4, 5} // 创建一个切片,引用数组的前三个元素
slice := array[:3] // 打印切片元素
fmt.Println(slice) // 输出: [1 2 3] // 修改切片元素,也会修改底层数组对应位置的元素
slice[1] = 100
fmt.Println(array) // 输出: [1 100 3 4 5]
}

指针的切片

  指针的切片是一个切片,但其元素是指针,指向某种数据类型的实例。这意味着每个元素都是一个地址,通过这个地址可以间接访问和操作实际的数据。指针的切片常用于需要存储大量数据且希望避免数据复制的场景,或者当需要在切片中存储可变大小的对象时。

  使用指针的切片可以节省内存空间,因为只需要存储指针而不是实际的数据。同时,通过指针可以方便地修改原始数据。然而,这也带来了额外的复杂性和风险,因为需要小心处理指针的解引用和内存管理。

  示例

package main  

import "fmt"  

type Person struct {
Name string
Age int
} func main() {
// 创建几个Person实例的指针
person1 := &Person{Name: "Alice", Age: 30}
person2 := &Person{Name: "Bob", Age: 25} // 创建一个指针的切片,存储这些指针
people := []*Person{person1, person2} // 通过指针修改Person实例的属性
people[0].Age = 31 // 打印修改后的Person实例
fmt.Println(people[0].Name, people[0].Age) // 输出: Alice 31
}

结构体切片与结构体指针切片的区别

  1. 内存占用:

    • 结构体切片:每个元素都是结构体的一个完整副本,因此内存占用较大。

    • 结构体指针切片:每个元素只是一个指向结构体的指针,内存占用较小。但是,这并不意味着整体内存占用会小,因为还需要考虑实际结构体对象所占用的内存。

  2. 修改元素:

    • 结构体切片:修改切片中的一个元素,将直接修改该元素的值,不会影响其他切片或原始结构体对象。

    • 结构体指针切片:修改切片中的一个指针指向的元素,将影响所有指向该元素的指针。如果多个切片或变量指向同一个结构体对象,修改该对象的内容将影响所有引用。

  3. 初始化与赋值:

    • 结构体切片:可以直接初始化并赋值。

    • 结构体指针切片:需要先初始化结构体对象,然后将对象的地址赋值给切片。

  4. nil与空切片:

    • 对于结构体指针切片,nil切片和空切片(长度为0的切片)是不同的。nil切片没有分配底层数组,而空切片分配了底层数组但长度为0。

    • 对于结构体切片,通常不会讨论nil切片,因为切片总是与底层数组相关联。

能否直接遍历结构体切片并赋值给结构体指针切片

  不能直接遍历结构体切片并赋值给结构体指针切片。因为结构体切片中的元素是结构体的值,而结构体指针切片中的元素是指向结构体的指针。需要遍历结构体切片,并分别为每个元素创建指针,然后将这些指针添加到结构体指针切片中。

示例说明

  假设我们有一个Person结构体:

type Person struct {
Name string
Age int
}

  结构体切片的使用

// 创建并初始化一个Person结构体切片
peopleSlice := []Person{
{"Alice", 30},
{"Bob", 25},
} // 修改切片中的一个元素
peopleSlice[0].Age = 31 // 遍历并打印切片中的元素
for _, person := range peopleSlice {
fmt.Println(person.Name, person.Age)
}

  结构体指针切片的使用

// 创建并初始化一些Person结构体对象
alice := Person{"Alice", 30}
bob := Person{"Bob", 25} // 创建一个Person指针切片,并将结构体对象的地址添加到切片中
peoplePtrSlice := []*Person{&alice, &bob} // 修改切片中的一个指针指向的元素
peoplePtrSlice[0].Age = 31 // 这将改变alice的年龄,因为peoplePtrSlice[0]指向alice // 遍历并打印切片中的元素(通过解引用指针)
for _, personPtr := range peoplePtrSlice {
fmt.Println(personPtr.Name, personPtr.Age)
} // 如果想要遍历结构体切片并赋值给结构体指针切片,你需要这样做:
peopleSlice := []Person{
{"Charlie", 28},
{"David", 35},
} // 初始化一个空的Person指针切片,长度与peopleSlice相同
peoplePtrSlice = make([]*Person, len(peopleSlice)) // 遍历peopleSlice,为peoplePtrSlice分配新的指针
for i, person := range peopleSlice {
// 创建person的一个副本,并获取其地址,然后赋值给peoplePtrSlice
peoplePtrSlice[i] = &Person{Name: person.Name, Age: person.Age}
} // 现在peoplePtrSlice包含了指向peopleSlice中元素副本的指针

  在上面的示例中,peoplePtrSlice是通过遍历peopleSlice并创建每个元素的副本的地址来初始化的。如果你想要peoplePtrSlice中的指针指向peopleSlice中的相同对象(而不是副本),你需要确保这些对象是通过new函数或通过&操作符在堆上分配的,并且它们的地址被添加到peoplePtrSlice中。但是,在大多数情况下,你可能不希望这样做,因为这会导致切片之间共享相同的对象,从而可能引起意外的副作用。

能否直接遍历结构体指针切片并赋值给结构体切片

  不能直接遍历结构体指针切片并赋值给结构体切片。结构体指针切片中的元素是指向结构体的指针,而结构体切片中的元素是结构体的值。因此,如果你尝试直接将指针切片中的指针赋值给结构体切片,你会得到的是指针的值(即内存地址),而不是结构体对象本身的值。

  要遍历结构体指针切片并将指针指向的结构体对象赋值给结构体切片,你需要对每个指针进行解引用,获取其指向的结构体对象,然后将该对象赋值给结构体切片中的相应位置。

  下面是一个详细的示例说明和分析:

package main  

import (
"fmt"
) // 定义Person结构体
type Person struct {
Name string
Age int
} func main() {
// 初始化一些Person结构体对象,并获取它们的地址
alice := &Person{"Alice", 30}
bob := &Person{"Bob", 25} // 创建一个Person指针切片
peoplePtrSlice := []*Person{alice, bob} // 创建一个与peoplePtrSlice长度相同的Person结构体切片
peopleSlice := make([]Person, len(peoplePtrSlice)) // 遍历peoplePtrSlice,解引用指针并将结构体对象赋值给peopleSlice
for i, personPtr := range peoplePtrSlice {
peopleSlice[i] = *personPtr // 解引用指针,获取结构体对象
} // 现在peopleSlice包含了peoplePtrSlice中指针指向的结构体对象的副本
fmt.Println(peopleSlice) // 输出: [{Alice 30} {Bob 25}] // 修改peopleSlice中的元素,不会影响peoplePtrSlice或原始结构体对象
peopleSlice[0].Age = 31
fmt.Println(peopleSlice) // 输出: [{Alice 31} {Bob 25}]
fmt.Println(peoplePtrSlice) // 输出: [&{Alice 30} &{Bob 25}],peoplePtrSlice中的元素未改变
}

  在这个示例中:

  1. 我们首先创建了两个Person结构体对象的指针alicebob
  2. 然后我们创建了一个包含这两个指针的peoplePtrSlice切片。
  3. 接着我们创建了一个与peoplePtrSlice长度相同的空Person结构体切片peopleSlice
  4. 在遍历peoplePtrSlice时,我们使用*personPtr来解引用指针,并将得到的结构体对象赋值给peopleSlice中的对应位置。
  5. 最后,我们验证了修改peopleSlice中的元素不会影响peoplePtrSlice或原始的结构体对象。

  需要注意的是,这样做会创建结构体对象的副本。如果你只是想引用原始的对象而不是它们的副本,你应该直接使用指针切片,而不是创建结构体切片。如果你确实需要结构体切片,并且想保持对原始对象的引用,那么你需要重新考虑你的数据结构设计,因为结构体切片本身不保存对原始对象的引用。

  

go切片和指针切片的更多相关文章

  1. go语言切片切片与指针

    go语言 1.切片的定义 切片不是真正意义上的动态数组,是引用类型. var arraySlice []int

  2. 【玩转Golang】slice切片的操作——切片的追加、删除、插入等

    一.一般操作 1,声明变量,go自动初始化为nil,长度:0,地址:0,nil func main(){ var ss []string; fmt.Printf("length:%v \ta ...

  3. Python-序列切片原理和切片协议-[start:end:step] __getitem__

    切片原理图(顾头不顾尾的正则原理) # [0:1] 其实只取到C, 取e则 [-1:], 如果步长为负数则倒过来取,从第几个往回取 name = "ChuiXue" print(n ...

  4. GO语言学习——切片三 append()方法为切片添加元素、切片的扩容策略、使用copy()函数复制切片、从切片中删除元素

    append()方法为切片添加元素 注意:通过var声明的零值切片可以在append()函数直接使用,无需初始化. var s []int s = append(s, 1, 2, 3) append( ...

  5. redis-手写redis切片和非切片连接池并注入springboot中

    spring-data整合了redispool, 并提供redisTemplate使用, 但有时需要用到shradedJedisPool, 就需要手动注入了 手写redispool并注入springb ...

  6. ArcGIS切片服务获取切片方案xml文件(conf.xml)

    在使用ArcGIS进行影像.地形等切片时,往往需要保持一致的切片方案才能够更好的加载地图服务. 本文介绍如何获取已经发布好的ArcGIS服务的切片方案xml文件. 当然切片xml文件还可以通过工具Ge ...

  7. golang的指针和切片

    首先为什么要讲go的指针和切片放在一起? 因为go指针和切片都是引用类型 引用类型就是说切片和指针保存的只是内存的地址,而不是具体的值,效率在大数据读取方面效率会高很多. 1.怎么定义一个切片 方法1 ...

  8. go语言笔记——切片底层本质是共享数组内存!!!绝对不要用指针指向 slice切片本身已经是一个引用类型就是指针

    切片 切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此更类似于 C/C++ 中的数组类型,或者 Python 中的 list 类型) ...

  9. Go-常识补充-切片-map(类似字典)-字符串-指针-结构体

    目录 Go 常识补充 Go 命名 打印变量类型科普 _ 关键字 命名规范相关 包目录规范 切片 多维切片 切片初始化的方法 多维切片初始化 切片删除元素(会略微影响效率 ,少用) copy 函数 打散 ...

  10. golang笔记——数组与切片

    一.切片的定义 我们可以从数组(go语言中很少直接使用数组)或者切片来初始化一个新的切片,也可以直接通过 make 来初始化一个所有元素为默认零值的切片. //1.通过数组来初始化切片 arr := ...

随机推荐

  1. C#系列文章索引

    由于有读者说,是否可以讲C#一类的文章都统一在一起,因此我做了个索引文章置顶起来,以后C#相关文章也会同步更新到该索引文章下,以便查找 .Neter所应该彻底了解的委托 - RyzenAdorer - ...

  2. Java 抽象类的应用:模板方法的设计模式

    1 package com.bytezreo.template; 2 3 /** 4 * 5 * @Description 抽象类的应用:模板方法的设计模式 6 * @author Bytezero· ...

  3. 13 Codeforces Round 886 (Div. 4)G. The Morning Star(简单容斥)

    G. The Morning Star 思路:用map记录x,y,以及y-x.y+x 从前往后统计一遍答案即可 公式\(ans+=cnt[x]+cnt[y]-2 * cnt[x,y]+cnt[y+x] ...

  4. 获取一段时间内,以月/季度为单位,第N天在各个月/季度是几几年几月几号

    /** * 获取一段时间内(可跨年),以季度为单位,第N天在各个季度是几月几号 * @param $sTime 时间戳 * @param $eTime 时间戳 * @param $number 第N天 ...

  5. mybatis使用postgresql中的jsonb数据类型

    最近新开发的一个功能使用到postgresql中的jsonb数据类型.架构师可能考虑到这种数据格式更加便于存储json格式的数据,因此考虑使用这种数据类型.自己以前未曾使用过这种数据类型,因此需要现学 ...

  6. python文件获取并读取固定长度数据实例解析

    一 概念 1 file 操作: 文件操作一般有open,write,read,close几种,这里重点是read固定长度数据. read() 用于从文件读取指定的字节数,如果未给定或为负则读取所有. ...

  7. 教你一招,解决Github图片不显示问题(2021.1.20测试可用)

    本文提供的是windows系统解决方法,linux系统和mac系统可以参考原理,修改DNS的ip地址为阿里云或者是修改hosts文件 问题 可能有些朋友和我遇到同样的问题,逛Github的时候会发现, ...

  8. 基于webpack与TypeScript的SolidJS项目搭建

    本文将讲述如何基于webpack与TypeScript搭建一个基础的支持less模块的solidjs项目.方便后续涉及到solidjs相关分析与讨论都可以基于本文的成果之上进行. 前置 nodejs ...

  9. Linux输入输出

    1.重定向概述 1.什么是重定向 将原本要输出到屏幕的数据信息,重新定向到某个指定的文件中.比如:每天凌晨定时备份数据,希望将备份数据的结果保存到某个文件中. 这样第二天通过查看文件的内容就知道昨天备 ...

  10. TP6框架--EasyAdmin学习笔记:项目上线

    这是我暂时写EasyAdmin的最后一章,给大家分享下项目上线的全过程,希望对大家有所帮助,废话不多说,直接上内容 服务器我选用的是阿里云,上线时我使用的是宝塔面板来进行部署,如果你是新手,并不熟练服 ...