Golang數據類型之數組


有過PythonJavaScript編程經驗的人都知道其數組是動態的,可以隨需求自動增大數組長度,而Go里面的數組長度卻是固定的,無法擴大或者縮小

Go中也有類似的動態"數組",稱為切片slice

Go中的數組是slicemap兩種數據類型的基礎,這兩種數據類型的底層都是通過數組實現的

1、存儲方式

當在Go中聲明一個數組之后,會在內存中開辟一段固定長度的、連續的空間存放數組中的各個元素,這些元素的數據類型完全相同,可以是內置的簡單數據類型(intstring等),也可以是自定義的struct`結構體類型

  • 固定長度:這意味着數組不可增長、不可縮減。想要擴展數組,只能創建新數組,將原數組的元素復制到新數組
  • 連續空間:這意味可以在緩存中保留的時間更長,搜索速度更快,是一種非常高效的數據結構,同時還意味着可以通過數值index的方式訪問數組中的某個元素
  • 數據類型:意味着限制了每個block中可以存放什么樣的數據,以及每個block可以存放多少字節的數據

例如,使用下面的語句聲明一個長度為4int類型的數組,那么這個數組最多只能存放4個元素,且所有元素都只能是int類型。同時,還為這個數組做了初始化

array := [4]int{3, 1, 4,1 }

2、聲明與初始化

2.1 聲明語法

Go語言數組聲明需要指定元素類型及元素個數,語法格式如下

var variable_name [SIZE] variable_type

比如聲明一個長度為5, 類型是float64的數組

var arrayf [5]float64

2.2 數組類型

雖然稱呼數組為int類型的數組,但數組的數據類型是兩部分組成的[n]TYPE,這個整體才是數組的數據類型。所以,[4]int[5]int是兩種不同的數組類型

var (
    a1 [4]int
    a2 [5]int
)
fmt.Println(reflect.TypeOf(a1))
fmt.Println(reflect.TypeOf(a2))

2.3 數組默認值

當一個變量被聲明之后, 都會立即賦予一個默認值, 數組的默認值和數組的數據類型有關

var a1 [5]int
fmt.Println(a1) // [0 0 0 0 0]
var a2 [4]string
fmt.Println(a2) // [   ]

2.4 聲明並初始化

如果不想填充默認值, 可以聲明時就賦值

a1 := [3]int{1, 2, 3}
fmt.Println(a1)
// 如果將元素個數指定為特殊符號...,則表示通過初始化時的給定的值個數來推斷數組長度
a2 := [...]int{1, 2, 3, 4}
fmt.Println(a2)
a3 := [...]int{1, 1, 1}
fmt.Println(a3)
// 如果聲明數組時,只想給其中某幾個元素初始化賦值,則使用索引號
a4 := [4]int{0: 1, 3: 5}
fmt.Println(a4)

3、訪問與修改

可以通過數組的索引訪問數組的值

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

同理可通過數組的索引修改數組的值

a[0] = 10
a[3] = 20
fmt.Println(a[0])
fmt.Println(a[3])

4、指針數組

可以聲明一個指針類型的數組,這樣數組中就可以存放指針。注意,指針的默認初始化值為nil

例如,創建int類型指針的數組

a := [4]*int{0: new(int), 3: new(int)}
fmt.Println(a)
// [0xc00001c2a8 <nil> <nil> 0xc00001c2b0]

// 如果指針地址為空, 是會報空指針錯誤的, 比如
// *a[1] = 3
// panic: runtime error: invalid memory address or nil pointer dereference 

*a[0] = 10
*a[3] = 20
fmt.Println(a)
fmt.Println(*a[0], *a[3])

// 為1賦值
a[1] = new(int)
*a[1] = 30
fmt.Println(a, *a[1])

5、數組拷貝

Go中,由於數組算是一個值類型,所以可以將它賦值給其它數組
因為數組類型的完整定義為[n]TYPE,所以數組賦值給其它數組的時候,nTYPE必須相同

修改a2數組元素的值,不會影響a1數組

例如:

a1 := [4]string{"a", "b", "c", "m"}
a2 := [4]string{"x", "y", "z", "n"}
a1 = a2
fmt.Println(a1, a2)

數組賦值給其它數組時,實際上是完整地拷貝一個數組。所以,如果數組是一個指針型的數組,那么拷貝的將是指針數組,而不會拷貝指針所指向的對象

a1 := [4]*string{new(string), new(string), new(string), new(string)}
a2 := a1
fmt.Println(a1, a2)

*a1[0] = "A"
*a1[1] = "B"
*a1[2] = "C"
*a1[3] = "D"
fmt.Println(*a1[0], *a2[0])
// A A

6、數組遍歷

range關鍵字可以對array進行迭代,每次返回一個index和對應的元素值。可以將range的迭代結合for循環對array進行遍歷

a := [4]int{1, 2, 3, 4}
for i, v := range a {
    fmt.Println(i, v)
}
/*
0 1
1 2
2 3
3 4
*/

7、多維數組

可以通過組合兩個一維數組的方式構成二維數組, 二維數據還是比較常用,

比如定義坐標, 表示4個坐標(1,1) (2,2) (3,3) (4,4)

// pos := [4][2]int{{1, 1}, {2, 2}, {3, 3}, {4, 4}}
fmt.Println(pos)
// [[1 1] [2 2] [3 3] [4 4]]

// 修改第一點的坐標
pos[0] = [2]int{10, 10}
fmt.Println(pos)

// [[10 10] [2 2] [3 3] [4 4]]

8、數組作為函數參數

Go中的傳值方式是按值傳遞,這意味着給變量賦值、給函數傳參時,都是直接拷貝一個副本然后將副本賦值給對方的。這樣的拷貝方式意味着:

  • 如果數據結構體積龐大,則要完整拷貝一個數據結構副本時效率會很低
  • 函數內部修改數據結構時,只能在函數內部生效,函數一退出就失效了,因為它修改的是副本對象

示例

func TestMain1(t *testing.T) {
	payload := [4]int{1}
	fmt.Printf("%p\n", &payload)  // 0xc00014a040
	change(payload)  // 0xc00014a060
}

func change(payload [4]int) {
	fmt.Printf("%p\n", &payload)
	payload[0] = 10
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM