golang数组

数组

概述

Golang的数组不是可变数组,而是严格遵循了数据结构的定义。所以他具有以下特点:

数据结构中数组的定义:

数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。

所以golang中数组具有如下性质:

1.数组储存同类型的元素。
"原因":数组要开辟连续且明确的空间,数据类型一样就可以通过元素个数*数据大小得到要开辟的空间大小。

2.数组的长度一旦确定就不可改变。
"原因":数组开辟的连续空间是不会增加或减少的,通过最初指定的长度计算出空间大小后就会确定下来。

3.数组一旦确定就不可改变,数组的确定需要满足以下两点:
"元素类型":用于计算每项的偏移量
"长度":偏移的次数

至于偏移的开始位置,golang会自行决定。

由于数组定义后不可变的特性,golang将数组设定为值类型。

数组的初始化

声明数组

基本语法

按照数组的定义很容易得知数组的声明格式。

var 数组变量名 [数组长度]元素类型;

举例

package array_knowledge

import "fmt"

func CreateArr(){
	//声明数组
	var arr1 [5]string;
	fmt.Printf("arr1的值为%#v\n",arr1)
}

调用

package main
import (
	"go_learn/array_knowledge"
)
func main(){
    //声明数组
	array_knowledge.CreateArr()
}

结果

arr1的值为[5]string{"", "", "", "", ""}

数组声明了却没有赋值会用类型的零值填充元素。

声明数组并赋值

基本语法

//完整赋值格式 注意赋值类型和赋值个数
var 数组变量名 [数组长度]元素类型 = [数组长度]元素类型{元素1,元素2,...}

由于赋值时值已经表明类型和元素个数,所以左边不用显式指明类型。

var 数组变量名 = [数组长度]元素类型{元素1,元素2,...}

简易声明并赋值

数组变量名 := [数组长度]元素类型{元素1,元素2,...}

举例

package array_knowledge

import "fmt"

func CreateArr(){
	//声明并赋值--完整版
	var arr2 [3]int64 = [3]int64{1,2,3}

	//声明并赋值--左边类型自推导
	var arr3 =[4]bool{true,false,true,false}

	//简易声明并赋值
	arr4 := [2]string{"hello","world"}
	fmt.Printf("arr2的值为%#v\n",arr2)
	fmt.Printf("arr3的值为%#v\n",arr3)
	fmt.Printf("arr4的值为%#v\n",arr4)
}

调用

package main
import (
	"go_learn/array_knowledge"
)
func main(){
    //声明数组
	array_knowledge.CreateArr()
}

结果

arr2的值为[3]int64{1, 2, 3}
arr3的值为[4]bool{true, false, true, false}
arr4的值为[2]string{"hello", "world"}

注意事项

1.数组赋值的元素个数不能超过数组长度。(数组的内存空间固定,多了放不下会报错)
2.数组赋值的元素类型必须是数组类型指定元素。

数组赋值

赋值元素数量不够

当我们赋值的元素个数少于数组长度时将按照位置一一赋值,后续的用零值填充。

举例

package array_knowledge

import "fmt"

func CreateArrVal(){
	//赋值元素个数少于数组长度
	var arr1 = [4]int{1,2}
	fmt.Printf("arr1的值为%#v\n",arr1)
}

调用

package main
import (
	"go_learn/array_knowledge"
)
func main(){
    //声明数组
	array_knowledge.CreateArr()
}

结果

arr1的值为[4]int{1, 2, 0, 0}

总结

1.赋值元素个数不能超过数组长度。
2.赋值元素个数少于数组长度时后续元素将用零值填充。
使用下标赋值

等价于直接给下标赋值,语法

var 数组变量 = [数组长度]类型{下标A:1,下标B:2}
//等价于
var 数组变量 [数组长度]类型;
数组[下标A] =1
数组[下标B] =2

由此我们可以知道,只要不越界就是合法的。

举例

func CreateArrVal(){
	//使用下标赋值
	var arr2 = [5]string{1:"world",4:"happy"}
	fmt.Printf("arr2的值为%#v\n",arr2)	
}

结果

arr2的值为[5]string{"", "world", "", "", "happy"}
下标与位置混用

不推荐这种写法:这里只是做了解。

规则

1.第一个出现的位置元素,如果前面没有下标元素,则默认从0开始,此时再声明下标0的则为非法。

//"sad"元素左边没有下标元素,所以从0开始
var arr3 = [5]string{"sad",2:"world","hello",4:"happy"}

//报错:"sad"已经占据了下标0,下标0元素赋值失败
var arr3 = [5]string{"sad",0:"haha",2:"world","hello",4:"happy"}

2.如果位置元素左边有下标元素,则后续出现的位置元素,从下标元素处计算

//"hello"出现在1:"world"后,所以为2:"hello"
var arr3 = [5]string{0:"haha",1:"world","hello",4:"happy"}
//等价于
var arr3 = [5]string{0:"haha",1:"world",2:"hello",4:"happy"}

3.注意下标重复问题

var arr3 = [5]string{0:"haha",2:"world","hello","sad",4:"happy"}
//下标自动往后推,所以等价于
//报错:下标4重复了
var arr3 = [5]string{0:"haha",2:"world",3:"hello",4:"sad",4:"happy"}

4.注意乱序问题,乱序后的位置元素计算逻辑仍然是相对最近的计算。

var arr3 = [5]string{2:"haha",0:"world","hello",4:"happy"}
//等价于
var arr3 = [5]string{0:"world",1:"hello",2:"haha",4:"happy"}

总结

1.位置元素的下标会根据左边最近的下标元素推导,如果没有就从0开始
2.得到最后的全含下标的数组元素,如果出现下标重复或者下标越界就报错。

不定长数组

有时我们可能无法确定数组具体长度,此时可以通过第一次赋值得到数组长度。

语法

var 数组变量 = [...]类型{元素1,元素2,...}

这种方式声明数组必须同时赋值,通过赋值来确定最终的数组个数,例如

var arr2 = [...]int{1,2,3,4}
//最终等价于
var arr2 = [4]int{1,2,3,4}

我们也可以采用下标赋值

var arr5 =[...]string{5:"a",10:"b"}
//最终将采用最大的下标来得到数组,数组长度=最大下标+1
//注意下标不能重复,且不能出现上述混用的错误
var arr5 = [11]string{5:"a",10:"b"}

举例

package array_knowledge

import "fmt"

func CreateArrVal(){
	//不定长数组
	var arr4 = [...]int64{1,2,3,4}
	fmt.Printf("arr4的值为%#v\n",arr4)

	//用下标设置不定长数组
	var arr5 =[...]string{5:"a",10:"b"}
	fmt.Printf("arr5的值为%#v\n",arr5)
}

调用

package main
import (
	"go_learn/array_knowledge"
)
func main(){
    //声明数组
	array_knowledge.CreateArrVal()
}

结果

arr4的值为[4]int64{1, 2, 3, 4}
arr5的值为[11]string{"", "", "", "", "", "a", "", "", "", "", "b"}

数组的访问与修改

数组可以通过下标来访问并修改元素。

//访问元素
数组变量[下标];

//如果该下标原来没有赋值就是零值
//该操作属于修改数组的值
数组变量[下标]=;

举例

package array_knowledge
import "fmt"
func GetArrVal(){
	var arr_new = [3]string{"a","b","c"}
	arr_new[2] = "d"
	fmt.Printf("arr_new的值为%#v\n",arr_new)
}

结果

arr_new的值为[3]string{"a", "b", "d"}

数组类型

数组变量的最终类型是

[数组长度]类型
//例如
[3]string
[2]map[string]int

注意事项

数组类型是由数组长度和元素类型组成的,二者要都相同才属于同一数组类型,否则就是不同数组类型。

即使是不定长数组再最终也会根据赋值计算出长度

//属于同一数组类型
var a [2]int64
var b [2]int64

//属于不同数组类型
var c [2]int64
var d [2]int32 //元素类型不同
var e [3]int64 //元素长度不同

数组作为参数

这里要与js和python进行区分,golang中数组是值类型,所以直接作为参数传递不会影响到外部原始数组。

举例

package array_knowledge

import "fmt"

func ChangeArr(arr [3]string){
	arr[2] = "gender"
	fmt.Printf("函数内部的数组为%#v\n",arr)
}

调用

package main

import (
	"fmt"
	"go_learn/array_knowledge"
)

//这是入口文件
func main(){
	//测试数组是值类型
	var arrA = [3]string{"name","age","sex"}
	array_knowledge.ChangeArr(arrA)
	fmt.Printf("函数外部的数组为%#v\n",arrA)
}

结果

函数内部的数组为[3]string{"name", "age", "gender"}
函数外部的数组为[3]string{"name", "age", "sex"}

结果证明数组是值类型,函数内修改数组不会影响到原始数组。

使用指针类型传递参数

要让函数内能修改值类型的原始变量,我们需要借助指针类型。

注意

//这是数组的指针类型
*[长度]类型

//这是存储指针类型元素的数组
[长度]*类型

举例

package array_knowledge
import "fmt"
func ChangeArrByPtr(arrPtr *[3]string){
	//数组与一般值类型的区别
	/*
		一般值类型的修改是 *指针变量 = 新值
		但是数组是直接 指针类型变量 = 新值

		省略了取内容的过程,不仅如此,对数组使用`*指针类型变量`来修改是错误的
	*/
	arrPtr[2] = "gender"
    //修改的时候使用指针变量,但是访问值还是要取*
	fmt.Printf("函数内部的数组为%#v\n",*arrPtr)
}

调用

package main

import (
	"fmt"
	"go_learn/array_knowledge"
)

//这是入口文件
func main(){
	//通过指针修改数组
	var arrB = [3]string{"name","age","sex"}
	array_knowledge.ChangeArrByPtr(&arrB)
	fmt.Printf("函数外部的数组为%#v\n",arrB)
}

结果

函数内部的数组为[3]string{"name", "age", "gender"}
函数外部的数组为[3]string{"name", "age", "gender"}

注意

一般值类型的修改是 *指针变量 = 新值
但是数组是直接 指针类型变量 = 新值

数组省略了取内容的过程,不仅如此,对数组使用`*指针类型变量`来修改是错误的

例如

//使用值类型的指针传参实现对原始变量的修改
func ChangeByPtr(a *int64){
	//通过指针变量来获取原始值,*是取内容
	*a += 5
}

这一点对结构体也适用,所以在值类型中用指针类型变量操作数组和结构体与其他值类型是不一样的。

个人感觉这个golang的设计者故意留下的区别。

理解

1.用指针类型变量来`操作`结构体和数组时,直接使用指针类型变量。
2.但是要访问最终值仍然需要`*指针类型变量`

数组的长度和容量

数组的长度和容量是一样的,语法

//获取数组长度
len(数组)
//获取数组容量
cap(数组)

这两个都是golang内置方法,容量对于以后判断是否为同一类切片具有极其关键的作用。

举例

package array_knowledge
import "fmt"
func CapandLen(){
	var arr [5]int64;
	fmt.Printf("数组arr的容量为%v\n",cap(arr))
	fmt.Printf("数组arr的长度为%v\n",len(arr))
}

调用

package main

import (
	"fmt"
	"go_learn/array_knowledge"
)
func main(){
    //获取数组长度和容量
	array_knowledge.CapandLen()
}

结果

数组arr的容量为5
数组arr的长度为5

遍历数组

我们可以用for循环或者for…range遍历数组.

举例

package array_knowledge
import "fmt"
func GetEveryItem(){
	arr1 := [3]string{"I","am","one"}

	//用for循环遍历数组
	for i:=0;i<len(arr1);i++{
		fmt.Printf("for:第%d个数组元素为%s\n",i+1,arr1[i])
	}

	//用for...range遍历数组
	for idx,val := range arr1{
		fmt.Printf("range:第%d个数组元素为%s\n",idx+1,val)
	}
}

调用

package main

import (
	"fmt"
	"go_learn/array_knowledge"
)
func main(){
    array_knowledge.GetEveryItem()
}

结果

for:1个数组元素为I
for:2个数组元素为am
for:3个数组元素为one
range:1个数组元素为I
range:2个数组元素为am
range:3个数组元素为one
<think>首先,用户的问题是:“golang 数组取切片”,翻译成英文是“Golang array take slice”。我需要用中文回答这个问题。问题似乎是关于如何在Go语言中从数组获取切片。 关键点是: - 使用中文回答问题。 - 回答后,必须提出3-5个相关问题。 - 相关问题必须以“§§相关问题§§”开头,且在第一行完整输出。 - 禁止换行分散,意思是相关问题的列表应该紧凑,没有额外的换行。 - 最后,所有内容必须符合规则,否则回答无效。 回顾示例格式: - 首先,回答问题的内容。 - 然后,输出“§§相关问题§§”,紧接着是相关问题列表,如“1. ··· 2. ··· 3. ···”,没有换行分散。 所以,我的回答结构应该是: 1. 解释如何在Go中从数组获取切片。 2. 以中文回答。 3. 结束时,输出“§§相关问题§§”,然后列出3-5个相关问题。 现在,内容部分: - 在Go中,数组是固定大小的,切片是动态大小的视图。 - 要从数组获取切片,可以使用切片操作符:`array[start:end]`,其中start和end是索引。 - 例如:`arr := [5]int{1,2,3,4,5}`,然后 `slice := arr[1:4]` 会得到 `[2,3,4]`。 - 解释基本概念。 确保回答简洁明了。 然后,提出相关问题: - 相关问题应该基于用户的问题和我的回答。 - 用户的问题是“golang 数组取切片”,所以相关问题可能涉及: 1. 切片和数组的区别。 2. 如何修改切片影响原数组。 3. 切片的基本操作。 4. 性能考虑。 5. 常见错误。 列出3-5个: 1. Go语言中数组和切片的主要区别是什么? 2. 如何从切片创建新的切片? 3. 修改切片会影响原始数组吗?为什么? 4. 在Go中,切片的容量和长度如何工作? 5. 如何避免在切片操作中出现索引越界错误? 确保问题是相关的。 最后,输出格式: - 回答内容。 - 然后,直接“§§相关问题§§”后跟问题列表,例如: §§相关问题§§ 1. ... 2. ... 3. ... 没有额外的换行。 编写回答: - 先用中文解释。 - 可能包括一个简单的代码示例,因为用户提到了“golang”,类似于示例中的Python代码。 - 示例格式有代码块,所以我应该包括一个Go代码示例。 用户问题:“golang 数组取切片”,所以重点是操作。 代码示例: ```go package main import "fmt" func main() { // 定义一个数组 arr := [5]int{1, 2, 3, 4, 5} // 从数组取切片 slice := arr[1:4] // 索引从1开始到4(不包括4),所以是元素2,3,4 fmt.Println(slice) // 输出: [2 3 4] } ``` 在回答中包括这个。 然后结束。</think>在 Go 语言中,数组是固定大小的序列,而切片是动态大小的视图,可以基于数组创建。要从数组获取切片,使用切片操作符 `[start:end]`,其中 `start` 是起始索引(包含),`end` 是结束索引(不包含)。索引从 0 开始,如果省略 `start` 则默认为 0,省略 `end` 则默认为数组长度。切片是引用类型,修改切片会影响原数组。 示例代码: ```go package main import "fmt" func main() { // 定义一个数组 arr := [5]int{10, 20, 30, 40, 50} // 从数组取切片:索引1到3(不包含3) slice := arr[1:3] fmt.Println(slice) // 输出: [20 30] // 修改切片会影响原数组 slice[0] = 99 fmt.Println(arr) // 输出: [10 99 30 40 50] } ``` 在这个例子中,`arr[1:3]` 创建了一个包含原数组索引 1 和 2 元素的切片。切片共享底层数组数据,因此对切片的修改会反映到原数组上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值