本節(jié)介紹Golang的基本數(shù)據(jù)類型,字符串,整型,浮點(diǎn)型,數(shù)組,切片
| 類型 | 長(zhǎng)度(字節(jié) byte) | 默認(rèn)值 | 說明 |
|---|---|---|---|
| bool | 1 | false | |
| byte | 1 | 0 | uint8 |
| rune | 4 | 0 | Unicode Code Point, int32 |
| int, uint | 8 | 0 | 32 或 64 位 |
| int8, uint8 | 1 | 0 | -128 ~ 127, 0 ~ 255,byte是uint8 的別名 |
| int8, uint8 | 1 | 0 | -128 ~ 127, 0 ~ 255,byte是uint8 的別名 |
| int16, uint16 | 2 | 0 | -32768 ~ 32767, 0 ~ 65535 |
| int32, uint32 | 4 | 0 | -21億~ 21億, 0 ~ 42億,rune是int32 的別名 |
| int64, uint64 | 8 | 0 | |
| float32 | 4 | 0.0 | |
| float64 | 8 | 0.0 | |
| complex64 | 8 | ||
| complex128 16 | |||
| uintptr | 4或8 | 以存儲(chǔ)指針的 uint32 或 uint64 整數(shù) | |
| array | 值類型 | ||
| struct | 值類型 | ||
| string | "" | UTF-8 字符串 | |
| slice | nil | 引用類型 | |
| map | nil | 引用類型 | |
| channel | nil | 引用類型 | |
| interface | nil | 接口 | |
| function | nil | 函數(shù) |
注意項(xiàng):
- float32大約可以提供小數(shù)點(diǎn)后6位的精度,作為對(duì)比,float64可以提供小數(shù)點(diǎn)后15位的精度。通常情況應(yīng)該優(yōu)先選擇float64,因此float32的精確度較低,在累積計(jì)算時(shí)誤差擴(kuò)散很快,而且float32能精確表達(dá)的最小正整數(shù)并不大,因?yàn)楦↑c(diǎn)數(shù)和整數(shù)的底層解釋方式完全不同 https://studygolang.com/articles/6429
一,字符串
1,字符串長(zhǎng)度與拼接
var name = "小明!"
say := "Hello,This is my City"
fmt.Println(len(name)) // 9 一個(gè)漢字 占3個(gè)字節(jié)
fmt.Println(len(say)) // 21 英文字母和空格,都占一個(gè)字節(jié)
fmt.Println(name + " " + say) // + 號(hào)可用于字符串拼接 // 小明! Hello,This is my City
fmt.Printf("%v %v \n",name,say) // 小明! Hello,This is my City
2,字符串分割
arr := strings.Split(say," ") //返回一個(gè)切片:[]string
fmt.Println(arr) // [Hello,This is my City]
fmt.Println(len(arr)) // 4
fmt.Println(cap(arr)) // 4
3,判斷是否包含
exist := strings.Contains(name,"小")
fmt.Println(exist) // true
4,判斷子串出現(xiàn)位置,如果沒有出現(xiàn),返回 -1
idx := strings.Index(say,"i") //第一個(gè)出現(xiàn)的位置
fmt.Println(idx) // 8 ,是從 0 開始的
fmt.Println(strings.LastIndex(say,"i")) // 18
5,切片合并成字符串
fruits := []string{"蘋果","橘子","香蕉"}
fmt.Println(strings.Join(fruits,",")) // 蘋果,橘子,香蕉
fmt.Println(reflect.TypeOf(strings.Join(fruits,","))) // string
6,字符串轉(zhuǎn)整形(有兩個(gè)返回值,處理err)
var strNum1 = "123"
var strNum2 = "123.456"
num1,err := strconv.Atoi(strNum1)
if err != nil {
num1 = 0
}
fmt.Println(num1) // 123
num2,err2 := strconv.Atoi(strNum2)
if err2 != nil {
fmt.Println(err2) //strconv.Atoi: parsing "123.456": invalid syntax
num2 = 0 //被觸發(fā),同理,strNum2 = "123.0"也會(huì)被觸發(fā)
}
fmt.Println(num2) // 0
7,字符串轉(zhuǎn)浮點(diǎn)型:
num3,err3 := strconv.ParseFloat(strNum1,32)
if err3 != nil {
num3 = 0
}
fmt.Println(num3) // 123
num4,err4 := strconv.ParseFloat(strNum2,64)
if err4 != nil {
num4 = 0
}
fmt.Println(num4) //123.456
二,整形與浮點(diǎn)型
1,數(shù)字類型之間互相轉(zhuǎn)換:
var num6 int64 = 100
var num7 float64 = 123.356
fmt.Println(reflect.TypeOf( int32(num6) )) // int32
fmt.Println(reflect.TypeOf( float64(num6) )) //float64
同理,還有int8() , int16() , int64() , float32() 等函數(shù)
2,整形轉(zhuǎn)字符串:
str1 := strconv.Itoa( int(num6) ) //這個(gè)函數(shù)只能傳入int類型的,不能傳入int64等其他整型的,淦
fmt.Println(str1) // 100
3,浮點(diǎn)型轉(zhuǎn)字符串:
str2 := strconv.FormatFloat(num7,'f',2,64)
fmt.Println(str2) // 123.36
三,數(shù)組
func main() {
// 1,聲明:元素類型 元素個(gè)數(shù)
var strList [5]string
fmt.Println(strList) //[ ] 聲明一個(gè)數(shù)組后會(huì)自動(dòng)設(shè)置其默認(rèn)值
var numList [5]int
fmt.Println(numList) //[0 0 0 0 0]
//2,聲明,并初始化
var list3 = [5]int{1,2,3}
fmt.Println(list3) //[1 2 3 0 0]
//3,聲明,初始化,自動(dòng)識(shí)別數(shù)組個(gè)數(shù):
var list4 = [...]int{1,2,3,4}
fmt.Println(list4) //[1 2 3 4]
fmt.Println(cap(list4)) //[1 2 3 4]
fmt.Println(len(list4)) //[1 2 3 4]
//4,函數(shù)傳參與遍歷
getList(list3)
}
func getList(list [5]int){
//第一種遍歷:
for i := 0; i < len(list); i++ {
fmt.Println(list[i]); // 1 2 3 0 0 //通過下標(biāo)訪問元素
}
//第二種遍歷:
for key, value := range list {
fmt.Printf("%v => %v \n",key,value) // 0 => 1
}
}
注意事項(xiàng):
- 1,數(shù)組:是同一種數(shù)據(jù)類型的固定長(zhǎng)度的序列。
- 2,長(zhǎng)度是數(shù)組類型的一部分,因此,var a[5] int和var a[10]int是不同的類型。
fmt.Println(reflect.TypeOf(list3)) //[5]int - 3,訪問越界,如果下標(biāo)在數(shù)組合法范圍之外,則觸發(fā)訪問越界,會(huì)panic
- 6,數(shù)組是值類型,賦值和傳參會(huì)復(fù)制整個(gè)數(shù)組,而不是指針。因此改變副本的值,不會(huì)改變本身的值。
- 7,支持 "=="、"!=" 操作符,因?yàn)閮?nèi)存總是被初始化過的。
- 8,指針數(shù)組 [n]*T,數(shù)組指針 *[n]T。
var list5 = [3]int{1,2,3}
var list6 = [3]int{1,2,3}
fmt.Println( list5 == list6 ) //true
var list7 = [3]int{1,2,4}
fmt.Println( list6 == list7 ) //false
1,多維數(shù)組:
var list8 = [2][3]int{
{1,2,3},
{7,8,9},
}
fmt.Println(list8) // [[1 2 3] [7 8 9]]
四,切片
需要說明,slice 并不是數(shù)組或數(shù)組指針。它通過內(nèi)部指針和相關(guān)屬性引用數(shù)組片段,以實(shí)現(xiàn)變長(zhǎng)方案。
- 切片:切片是數(shù)組的一個(gè)引用,因此切片是引用類型。但自身是結(jié)構(gòu)體,值拷貝傳遞。
- 切片的長(zhǎng)度可以改變,因此,切片是一個(gè)可變的數(shù)組。
- 切片遍歷方式和數(shù)組一樣,可以用len()求長(zhǎng)度。表示可用元素?cái)?shù)量,讀寫操作不能超過該限制。
- cap可以求出slice最大擴(kuò)張容量,不能超出數(shù)組限制。0 <= len(slice) <= len(array),其中array是slice引用的數(shù)組。
- 切片的定義:var 變量名 []類型,比如 var str []string var arr []int。
- 空切片等于nil :
fmt.Println(slice1 == nil) //true。 如果 slice == nil,那么 len、cap 結(jié)果都等于 0。
1,切片的各種定義:
//切片的聲明:1,聲明一個(gè)不定長(zhǎng)數(shù)組,
var slice1 []int
fmt.Println(slice1) // []
fmt.Println(slice1 == nil) //true
slice1 = append(slice1,1) //增加一個(gè)元素
fmt.Println(slice1) // [1]
//切片的聲明:2,通過make函數(shù)創(chuàng)建
var slice2 []int = make([]int,2)// []數(shù)據(jù)類型 ,長(zhǎng)度
fmt.Println(slice2)
//slice1[2] = 4 //注意:不可以直接為不存在的鍵賦值,需要通過append增加后才能 set,get
slice2 = append(slice2,4)
fmt.Println(slice2[2]) //4
//通過截取數(shù)組,來生成切片:slice = num[startIndex:endIndex] 如果不填則為默認(rèn)值:num[0 : max]
var num = [5]int{1,3,5,7,9}
var slice3 = num[:]
fmt.Println(slice3) //[1 3 5 7 9]
slice3 = num[1:]
fmt.Println(slice3) //[3 5 7 9] //可以看到,包含startIndex
slice3 = num[:2]
fmt.Println(slice3) //[1 3] //可以看到,不包含endIndex
2,切片的長(zhǎng)度
- len()函數(shù)可以獲取切片當(dāng)前長(zhǎng)度,cap()函數(shù)可以獲取切片的最大長(zhǎng)度
var x []int = make([]int,3,5)
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) //len=3 cap=5 slice=[0 0 0]
x = append(x,1)
x = append(x,2)
x = append(x,3)
x = append(x,4)
x = append(x,5)
x = append(x,6)
x = append(x,1) //經(jīng)過試驗(yàn)可以發(fā)現(xiàn),make定下的最大長(zhǎng)度cap = 5,其實(shí)是可以增加到6,7個(gè)元素的,并不是最大只能存5個(gè)
x = append(x,1) //當(dāng)切片長(zhǎng)度超過cap時(shí),cap會(huì)增長(zhǎng)一倍,就是用自己乘以2,直到放得下所增加的元素
fmt.Println(x)
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) //len=11 cap=20 slice=[0 0 0 1 2 3 4 5 6 1 1]
3,遍歷
切片的遍歷與上面數(shù)組相同
語言范圍Range:
- 1,語法: key,value := range + 變量 : 該用法返回變量中的 元素的 索引和值,如果是集合,則返回key - value 對(duì)
- 2,range + 變量,他有兩個(gè)返回值,如果某一個(gè)返回值你不需要用到,用 _ 接收
4,切片的本質(zhì)
func main() {
slice := []int{1, 2, 3, 4, 5}
fmt.Printf("切片:%v\n", slice) //切片:[1 2 3 4 5]
fmt.Printf("slice pointer is : %p \n",slice) // slice pointer is : 0xc00000a2d0
fmt.Printf("&slice pointer is : %p \n",&slice) // &slice pointer is : 0xc0000044a0
changeSlice1(slice)
fmt.Printf("切片:%v\n", slice) //切片:[1 2 10 4 5]
fmt.Printf("slice pointer is : %p \n",slice) // slice pointer is : 0xc00000a2d0
fmt.Printf("&slice pointer is : %p \n",&slice) // &slice pointer is : 0xc0000044a0
}
func changeSlice1(slice []int) {
slice[2] = 10
fmt.Printf("切片:%v\n", slice) //切片:[1 2 10 4 5]
fmt.Printf("slice pointer is : %p \n",slice) // slice pointer is : 0xc00000a2d0
fmt.Printf("&slice pointer is : %p \n",&slice) // &slice pointer is : 0xc000004520 指針已經(jīng)不同,可見發(fā)生了拷貝
}
- 1,由上面的代碼中可以看到,打印傳入函數(shù)的slice(值),一直都是 0xc00000a2d0 ,但是這個(gè)值的指針:&slice,是不一樣了的,證明值經(jīng)過了拷貝。那為什么函數(shù)里面的切片的改變,導(dǎo)致了調(diào)用處的切片也一并改變了呢?因?yàn)榍衅谋举|(zhì)就是一個(gè)由:保存該切片元素的數(shù)組的指針(也就是上面打印出來的0xc00000a2d0 ) + len + cap 的結(jié)構(gòu)體組成,它傳了這個(gè)值(0xc00000a2d0)進(jìn)去,進(jìn)而函數(shù)changeSlice1()對(duì)這個(gè)值所指向的真實(shí)數(shù)組做處理,從而,導(dǎo)致了調(diào)用出的切片的值也發(fā)送了改變。
- 2,Go的參數(shù)傳遞都是值傳遞,參考資料:https://blog.csdn.net/chen_peng7/article/details/89247047
image.png
