go语言基础-数组、字符串、切片

Go语言中数组长度固定,使用较少,更常见的是切片,它像动态数组一样灵活。字符串是不可变的字节序列,常与strings包配合使用。切片通过Data、Len和Cap三个字段表示,append函数用于添加元素,可能涉及内存重新分配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组

数组定义:一个由固定长度的特定类型元素组成的序列。因为数组的长度是数组类型的一部分,不同长度或者不同类型组成的数组都是不同的类型(即:不同长度的数组因为类型不同无法直接赋值),所以在go语言中很少直接使用数组,使用切片比较多,切片比较灵活。

定义和初始化

a := [3]int{}
b := [...]int{1, 2, 3}

第一种方式数组长度明确指定,数组中的每个元素都以零值初始化

第二种方式在定义的时候指定全部元素的初始化值,数组长度根据初始化元素的数目自动计算

值得注意的是:Go语言中一个数组变量代表整个数组,它并不是隐式地指向第一个元素的指针(例如C语言的数组)。当一个数组被赋值或者传递的时候,实际上是复制整个数组,因此有比较大的开销,为了避免这种情况,可以传递一个指向数组的指针。

a := [...]int{1, 2, 3}
    pr := &a //注意go语言中没有类似于c++引用的概念
    fmt.Println(a[0], a[1])
    fmt.Println(pr[0], pr[1]) //通过数组指针访问数组元素的方式与数组类似,go语言对指针进行了优化
    for _, v := range pr {
        fmt.Println(v)
    }

go语言对指针进行了优化,包括结构体指针对其成员变量访问方式均一致,使用起来很舒服。

字符串

字符串:一个不可改变的字节序列,和数组不同的是字符串的元素不可修改,是一个只读字节数组。Go语言源代码要求UTF8编码,因此字符串字面量一般也是UTF8编码的。

s := "hello world"
    hello := s[:5] //字符串虽然不是切片但是支持切片操作
    world := s[6:]
    fmt.Println(hello)
    fmt.Println(world)
    data := strings.Split(s, " ") //与strings包配合使用 空格进行分割
    fmt.Println(data)

字符串没啥可说的,配合strings这个包使用。

切片

简单来说,切片(slice)就是一个简化版的动态数组。因为切换的操作要比数组灵活的多,因此切片使用相当广泛,理解切片的原理和用法比较重要

切片的结构定义:

// SliceHeader is the runtime representation of a slice.
// It cannot be used safely or portably and its representation may
// change in a later release.
// Moreover, the Data field is not sufficient to guarantee the data
// it references will not be garbage collected, so programs must keep
// a separate, correctly typed pointer to the underlying data.
type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

切片多个一个Cap成员表示切片指向的内存空间的最大容量

去知乎找了一张图说明了切片的内存结构

声明和初始化

    var a []int //nil 切片,和nil相等
    if a == nil {
        fmt.Println("和nil相等")
    }
    b := []int{} //空切片 但是不是nil
    fmt.Println(b)
    c := []int{1, 2, 3}
    d := c[:2]
    fmt.Println(d)
    g := make([]int, 3) //有3个元素的切片,len和cap都为3 并元素初始化0
    fmt.Println(g)
    h := make([]int, 2, 3) //有2个元素的切片 len2 cap3 并元素初始化0
    fmt.Println(h)

添加切片元素

内置的泛型函数append()可以在切片尾部追加N个元素

    a := []int{}
    a = append(a, 1)
    fmt.Println(a)
    a = append(a, 2, 3, 4) //追加多个元素
    fmt.Println(a)
    a = append(a, []int{5, 6, 7}...) //追加一个切片切片需要解包
    fmt.Println(a)

值得注意的是在容量不足的情况下,append()操作可能会导致重新分配内存,造成巨大的内存分配和复制数据代价。

除了在尾部追加,还可以在切片的开头添加元素:

a := []int{}
    a = append(a, 1)
    a = append([]int{0}, a...)
    fmt.Println(a)

在开头添加一般都会导致内存重新分配,而且会导致已有元素全部复制一次,性能比较差一般不用

使用copy()和append()组合可以实现在中间位置插入多个元素(也就是插入一个切片)

    a := []int{1, 5, 6}
    x := []int{2, 3, 4}
    a = append(a, x...)       //扩充足够的空间
    copy(a[1+len(x):], a[1:]) //a[1:]向后移动len(x)个位置
    copy(a[1:], x)            //复制新添加的元素
    fmt.Println(a)

删除切片元素

根据删除元素的位置,分为开头位置删除、中间位置删除和从尾部删除三种情况,其中删除尾部元素最快

    a := []int{1, 2, 3}
    a = a[:len(a)-1] //删除倒数第一个元素
    fmt.Println(a)
    b := []int{1, 2, 3}
    b = b[1:] //删除开头第一个元素 ,直接移动数据指针
    fmt.Println(b)
    c := []int{1, 2, 3, 4, 5}
    c = append(c[:1], c[2:]...) //删除2这个元素
    fmt.Println(c)

切片内存使用技巧

len为0但是cap容量不为0的切片是非常有用的,例如下面的例子,高效 简洁的删除[]byte中的空格

func main() {
    str := "nihao baby nishi"
    trimStr := TrimSpace([]byte(str))
    fmt.Println(string(trimStr))
}

func TrimSpace(s []byte) []byte {
    b := s[:0] //len 为0 cap 为原始切片大小的切片
    fmt.Println(len(b)) //0
    fmt.Println(cap(b)) //32
    for _, c := range s {
        if c != ' ' {
            b = append(b, c)
        }
    }
    return b
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值