本节介绍Golang的基本数据类型,字符串,整型,浮点型,数组,切片
类型 | 长度(字节 byte) | 默认值 | 说明 |
---|---|---|---|
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 | 以存储指针的 uint32 或 uint64 整数 | |
array | 值类型 | ||
struct | 值类型 | ||
string | "" | UTF-8 字符串 | |
slice | nil | 引用类型 | |
map | nil | 引用类型 | |
channel | nil | 引用类型 | |
interface | nil | 接口 | |
function | nil | 函数 |
注意项:
- float32大约可以提供小数点后6位的精度,作为对比,float64可以提供小数点后15位的精度。通常情况应该优先选择float64,因此float32的精确度较低,在累积计算时误差扩散很快,而且float32能精确表达的最小正整数并不大,因为浮点数和整数的底层解释方式完全不同 https://studygolang.com/articles/6429
一,字符串
1,字符串长度与拼接
var name = "小明!"
say := "Hello,This is my City"
fmt.Println(len(name)) // 9 一个汉字 占3个字节
fmt.Println(len(say)) // 21 英文字母和空格,都占一个字节
fmt.Println(name + " " + say) // + 号可用于字符串拼接 // 小明! Hello,This is my City
fmt.Printf("%v %v \n",name,say) // 小明! Hello,This is my City
2,字符串分割
arr := strings.Split(say," ") //返回一个切片:[]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,判断子串出现位置,如果没有出现,返回 -1
idx := strings.Index(say,"i") //第一个出现的位置
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,字符串转整形(有两个返回值,处理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 //被触发,同理,strNum2 = "123.0"也会被触发
}
fmt.Println(num2) // 0
7,字符串转浮点型:
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
二,整形与浮点型
1,数字类型之间互相转换:
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()
等函数
2,整形转字符串:
str1 := strconv.Itoa( int(num6) ) //这个函数只能传入int类型的,不能传入int64等其他整型的,淦
fmt.Println(str1) // 100
3,浮点型转字符串:
str2 := strconv.FormatFloat(num7,'f',2,64)
fmt.Println(str2) // 123.36
三,数组
func main() {
// 1,声明:元素类型 元素个数
var strList [5]string
fmt.Println(strList) //[ ] 声明一个数组后会自动设置其默认值
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,声明,初始化,自动识别数组个数:
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,函数传参与遍历
getList(list3)
}
func getList(list [5]int){
//第一种遍历:
for i := 0; i < len(list); i++ {
fmt.Println(list[i]); // 1 2 3 0 0 //通过下标访问元素
}
//第二种遍历:
for key, value := range list {
fmt.Printf("%v => %v \n",key,value) // 0 => 1
}
}
注意事项:
- 1,数组:是同一种数据类型的固定长度的序列。
- 2,长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型。
fmt.Println(reflect.TypeOf(list3)) //[5]int
- 3,访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
- 6,数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
- 7,支持 "=="、"!=" 操作符,因为内存总是被初始化过的。
- 8,指针数组 [n]*T,数组指针 *[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,多维数组:
var list8 = [2][3]int{
{1,2,3},
{7,8,9},
}
fmt.Println(list8) // [[1 2 3] [7 8 9]]
四,切片
需要说明,slice 并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。
- 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
- 切片的长度可以改变,因此,切片是一个可变的数组。
- 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。
- cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。
- 切片的定义:var 变量名 []类型,比如 var str []string var arr []int。
- 空切片等于nil :
fmt.Println(slice1 == nil) //true
。 如果 slice == nil,那么 len、cap 结果都等于 0。
1,切片的各种定义:
//切片的声明:1,声明一个不定长数组,
var slice1 []int
fmt.Println(slice1) // []
fmt.Println(slice1 == nil) //true
slice1 = append(slice1,1) //增加一个元素
fmt.Println(slice1) // [1]
//切片的声明:2,通过make函数创建
var slice2 []int = make([]int,2)// []数据类型 ,长度
fmt.Println(slice2)
//slice1[2] = 4 //注意:不可以直接为不存在的键赋值,需要通过append增加后才能 set,get
slice2 = append(slice2,4)
fmt.Println(slice2[2]) //4
//通过截取数组,来生成切片:slice = num[startIndex:endIndex] 如果不填则为默认值: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,切片的长度
- len()函数可以获取切片当前长度,cap()函数可以获取切片的最大长度
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) //经过试验可以发现,make定下的最大长度cap = 5,其实是可以增加到6,7个元素的,并不是最大只能存5个
x = append(x,1) //当切片长度超过cap时,cap会增长一倍,就是用自己乘以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,遍历
切片的遍历与上面数组相同
语言范围Range:
- 1,语法: key,value := range + 变量 : 该用法返回变量中的 元素的 索引和值,如果是集合,则返回key - value 对
- 2,range + 变量,他有两个返回值,如果某一个返回值你不需要用到,用 _ 接收
4,切片的本质
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 指针已经不同,可见发生了拷贝
}
- 1,由上面的代码中可以看到,打印传入函数的slice(值),一直都是 0xc00000a2d0 ,但是这个值的指针:&slice,是不一样了的,证明值经过了拷贝。那为什么函数里面的切片的改变,导致了调用处的切片也一并改变了呢?因为切片的本质就是一个由:保存该切片元素的数组的指针(也就是上面打印出来的0xc00000a2d0 ) + len + cap 的结构体组成,它传了这个值(0xc00000a2d0)进去,进而函数changeSlice1()对这个值所指向的真实数组做处理,从而,导致了调用出的切片的值也发送了改变。
- 2,Go的参数传递都是值传递,参考资料:https://blog.csdn.net/chen_peng7/article/details/89247047