go切片和指针切片

简介: go切片和指针切片

转载请注明出处:

  在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. 内存占用:
  • 结构体切片:每个元素都是结构体的一个完整副本,因此内存占用较大。
  • 结构体指针切片:每个元素只是一个指向结构体的指针,内存占用较小。但是,这并不意味着整体内存占用会小,因为还需要考虑实际结构体对象所占用的内存。
  1. 修改元素:
  • 结构体切片:修改切片中的一个元素,将直接修改该元素的值,不会影响其他切片或原始结构体对象。
  • 结构体指针切片:修改切片中的一个指针指向的元素,将影响所有指向该元素的指针。如果多个切片或变量指向同一个结构体对象,修改该对象的内容将影响所有引用。
  1. 初始化与赋值:
  • 结构体切片:可以直接初始化并赋值。
  • 结构体指针切片:需要先初始化结构体对象,然后将对象的地址赋值给切片。
  1. 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

香吧香
+关注
目录
打赏
0
2
2
1
77
分享
相关文章
Go语言切片,使用技巧与避坑指南
Go语言中的切片(Slice)是动态引用数组的高效数据结构,支持扩容与截取。本文从切片基础、常用操作到高级技巧全面解析,涵盖创建方式、`append`扩容机制、共享陷阱及安全复制等内容。通过代码示例详解切片特性,如预分配优化性能、区分`nil`与空切片、处理多维切片等。掌握这些核心知识点,可编写更高效的Go代码。
62 2
Go语言实战案例 - 找出切片中的最大值与最小值
本案例通过实现查找整数切片中的最大值与最小值,帮助初学者掌握遍历、比较和错误处理技巧,内容涵盖算法基础、应用场景及完整代码示例,适合初学者提升编程能力。
|
1月前
|
Go语言中使用切片需要注意什么?
本文详细讲解了Go语言中切片(Slice)的使用方法与注意事项。切片是对数组连续片段的引用,具有灵活的操作方式。文章从定义与初始化、长度与容量、自动扩容、共享底层数组、复制、边界检查、零值到拼接等方面展开,并配以示例代码演示。通过学习,读者可深入了解切片的工作原理及优化技巧,避免常见陷阱,提升编程效率与代码质量。
74 2
[go]Slice 切片原理
本文详细介绍了Go语言中的切片(slice)数据结构,包括其定义、创建方式、扩容机制及常见操作。切片是一种动态数组,依托底层数组实现,具有灵活的扩容和传递特性。文章解析了切片的内部结构(包含指向底层数组的指针、长度和容量),并探讨了通过`make`创建切片、基于数组生成切片以及切片扩容的规则。此外,还分析了`append`函数的工作原理及其可能引发的扩容问题,以及切片拷贝时需要注意的细节。最后,通过典型面试题深入讲解了切片在函数间传递时的行为特点,帮助读者更好地理解和使用Go语言中的切片。
Go语言之切片的原理与用法 - 《Go语言实战指南》
切片(slice)是Go语言中用于处理变长数据集合的核心结构,基于数组的轻量级抽象,具有灵活高效的特点。切片本质是一个三元组:指向底层数组的指针、长度(len)和容量(cap)。本文详细介绍了切片的声明与初始化方式、基本操作(如访问、修改、遍历)、长度与容量的区别、自动扩容机制、共享与副本处理、引用类型特性以及常见陷阱。通过理解切片的底层原理,开发者可以更高效地使用这一数据结构,优化代码性能。
|
7月前
|
go语言中数组和切片
go语言中数组和切片
132 7
|
8月前
|
Go语言中,遍历数组或切片
在Go语言中,遍历数组或切片
170 6
|
5月前
|
Go 语言入门指南:切片
Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
136 3
Go 语言入门指南:切片
Go 切片导致 rand.Shuffle 产生重复数据的原因与解决方案
在 Go 语言开发中,使用切片时由于其底层数据共享特性,可能会引发意想不到的 Bug。本文分析了 `rand.Shuffle` 后切片数据重复的问题,指出原因在于切片是引用类型,直接赋值会导致底层数组共享,进而影响原始数据。解决方案是使用 `append` 进行数据拷贝,确保独立副本,避免 `rand.Shuffle` 影响原始数据。总结强调了切片作为引用类型的特性及正确处理方法,确保代码稳定性和正确性。
170 82
|
7月前
|
go语言for遍历数组或切片
go语言for遍历数组或切片
252 62
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问