Go语言学习笔记-Go数据类型

Go数据类型

1. 数据类型概览

1.1 基础数据类型

类型长度(字节)默认值说明
bool1false
byte10uint8,取值范围[0,255]
rune40Unicode Code Point, int32
int, uint4或8032 或 64 位,取决于操作系统
int8, uint810-128 ~ 127, 0 ~ 255
int16, uint1620-32768 ~ 32767, 0 ~ 65535
int32, uint3240-21亿~ 21亿, 0 ~ 42亿,rune是int32 的别名
int64, uint6480
float3240.0
float6480.0
complex648
complex12816
uintptr4或8以存储指针的 uint32 或 uint64 整数
// 只是声明,没有赋值(初始化)
func basic_data_type() {
	var a bool
	var b byte //uint8
	var c rune //4B     int32
	var d int  //8B

	//fmt.Printf("a %t %d\n", a, &a) //a false 824633835688
	//fmt.Printf("b %d %d\n", b, &b) //b 0 824633835689
	//fmt.Printf("c %d %d\n", c, &c) //c 0 824633835692
	//fmt.Printf("d %d %d\n", d, &d) //d 0 824633835712

	//fmt.Printf("b %T c %T\n", b, c) //b type c rune   b uint8 c int32

	fmt.Print("%\n")
	fmt.Println("%")
	fmt.Printf("%%\n")                            //上面三种都输出%
	fmt.Printf("a %t %d %p %x \n", a, &a, &a, &a) //%p是显示地址。如果哦想输出%本身,需要%%  //a false 824633819288 0xc000018098 c000018098
	fmt.Printf("b %d %d %p \n", b, &b, &b)        //b 0 824633819289 0xc000018099
	fmt.Printf("c %d %d %p \n", c, &c, &c)        //c 0 824633819292 0xc00001809c
	fmt.Printf("d %d %d %p \n", d, &d, &d)        //d 0 824633819312 0xc0000180b0

	c = 543434
	c = 'd'
	c = '中'

	b = 255
	//b = 256   //报错
	b = 'A'

	fmt.Printf("b=%c c=%c\n", b, c)                           //b=A c=中,通常使用%c来表示
	fmt.Printf("b=%d c=%d\n", b, c)                           //b=65 c=20013
	fmt.Printf("b=%U c=%U\n", b, c)                           //b=U+0041 c=U+4E2D
	fmt.Print(runtime.GOARCH, "  ", strconv.IntSize, a, b, c) //" "就是一个字面量 //amd64%!(EXTRA string=  , int=64, bool=false, uint8=65, int32=20013)
}

fmt.Printf("os arch %s, int size %d\n", runtime.GOARCH, strconv.IntSize) //int是4字节还是8字节,取决于操作系统是32位还是64位
var a int = 5
var b int8 = 5
var c int16 = 5
var d int32 = 5
var e int64 = 5
var f uint = 5
var g uint8 = 5
var h uint16 = 5
var i uint32 = 5
var j uint64 = 5
fmt.Printf("a=%d, b=%d, c=%d, d=%d, e=%d, f=%d, g=%d, h=%d, i=%d, j=%d\n", a, b, c, d, e, f, g, h, i, j)
var k float32 = 5
var l float64 = 5
fmt.Printf("k=%f, l=%.2f\n", k, l) //%.2f保留2位小数
var m complex128 = complex(4, 7)
var n complex64 = complex(4, 7)
fmt.Printf("type of m is %T, type of n is %T\n", m, n) //%T输出变量类型
fmt.Printf("m=%v, n=%v\n", m, n)                       //按值的本来值输出
fmt.Printf("m=%+v, n=%+v\n", m, n)                     //在 %v 基础上,对结构体字段名和值进行展开
fmt.Printf("m=%#v, n=%#v\n", m, n)                     //输出 Go 语言语法格式的值
fmt.Printf("m的实部%f, m的虚部%f\n", real(m), imag(m))
fmt.Printf("m的实部%e, m的虚部%g\n", real(m), imag(m)) //%e科学计数法,%g根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)
o := true                                        //等价于var o bool = true
fmt.Printf("o=%t\n", o)                          //%t布尔变量
var pointer unsafe.Pointer = unsafe.Pointer(&a)
var p uintptr = uintptr(pointer)
var ptr *int = &a
fmt.Printf("p=%x pointer=%p ptr=%p\n", p, pointer, ptr) //%p输出地址,%x十六进制
var q byte = 100                                        //byte是uint,取值范围[0,255]
fmt.Printf("q=%d, binary of q is %b\n", q, q)           //%b输出二进制
var r rune = '☻'                                        //rune实际上是int32,即可以表示2147483647种字符,包括所有汉字和各种特殊符号
fmt.Printf("r=%d, r=%U\n", r, r)                        //%U Unicode 字符
var s string = "I'm 张朝阳"
fmt.Printf("s=%s\n", s)
var t error = errors.New("my error")
fmt.Printf("error is %v\n", t)
fmt.Printf("error is %+v\n", t) //在 %v 基础上,对结构体字段名和值进行展开
fmt.Printf("error is %#v\n", t) //输出 Go 语言语法格式的值

  数值型变量的默认值是0,字符串的默认值是空字符串,布尔型变量的默认值是false,引用类型、函数、指针、接口的默认值是nil。数组的默认值取每个元素对应类型的默认值,结构体的默认值取每个成员变量对应类型的默认值。

var a int
var b byte
var f float32
var t bool
var s string
var r rune
var arr [3]int
var slc []int

fmt.Printf("default value of int %d\n", a)
fmt.Printf("default value of byte %d\n", b)
fmt.Printf("default value of float %.2f\n", f)
fmt.Printf("default value of bool %t\n", t)
fmt.Printf("default value of string [%s]\n", s)
fmt.Printf("default value of rune %d, [%c]\n", r, r)
fmt.Printf("default int array is %v\n", arr) //取每个元素对应类型的默认值
fmt.Printf("default slice is nil %t\n", slc == nil)

1.2 复合数据类型

类型默认值说明
array取每个元素对应类型的默认值值类型
struct取每个成员变量对应类型的默认值值类型
string“”UTF-8 字符串
slicenil引用类型
mapnil引用类型
channelnil引用类型
interfacenil接口
functionnil函数

2. 自定义类型

类型别名

type byte = uint8
type rune = int32
type semaphore = uint8
//type MAGE = int16 //类型别名
//var a MAGE        //var a int16
//a = 5436
//fmt.Printf("%d \n", a) //5436

自定义类型

type user struct {name string;age int}  //用分号把多行代码隔开
type signal uint8
type ms map[string]string
type add func(a, b int) int
//var b MAGE //var a init16
//b.cry()
//fmt.Printf("b %T\n", b) //b main.MAGE
//
//var a add
//a = foo
//fmt.Println(a(4, 7))    //11
//fmt.Printf("a %T\n", a) //%T type  // a main.add

3. 数组

  数组是块连续的内存空间,在声明的时候必须指定长度,且长度不能改变。所以数组在声明的时候就可以把内存空间分配好,并赋上默认值,即完成了初始化。

在这里插入图片描述

3.1 一维数组初始化

var arr1 [5]int = [5]int{} //数组必须指定长度和类型,且长度和类型指定后不可改变
var arr2 = [5]int{}
var arr3 = [5]int{3, 2} //给前2个元素赋值
var arr4 = [5]int{2: 15, 4: 30} //指定index赋值
var arr5 = [...]int{3, 2, 6, 5, 4}//根据{}里元素的个数推断出数组的长度
var arr6 = [...]struct {
    name string
    age int
}{{"Tom", 18}, {"Jim", 20}} //数组的元素类型由匿名结构体给定

3.2 二维数组初始化

//5行3列,只给前2行赋值,且前2行的所有列还没有赋满
var arr1 = [5][3]int{{1}, {2, 3}}
//第1维可以用...推测,第2维不能用...
var arr2 = [...][3]int{{1}, {2, 3}}

访问数组里的元素

  • 通过index访问
    • 首元素 arr[0]
    • 末元素 arr[len(arr)-1]
  • 访问二维数组里的元素
    • 位于第三行第四列的元素 arr[2][3]

3.3 遍历数组

//遍历数组里的元素
for i, ele := range arr {
    fmt.Printf("index=%d, element=%d\n", i, ele)
}
//或者这样遍历数组
for i := 0; i < len(arr); i++ { //len(arr)获取数组的长度
    fmt.Printf("index=%d, element=%d\n", i, arr[i])
}
//遍历二维数组
for row, array := range arr { //先取出某一行
    for col, ele := range array { //再遍历这一行
        fmt.Printf("arr[%d][%d]=%d\n", row, col, ele)
    }
}

  通过for range遍历数组时取得的是数组里每一个元素的拷贝。

arr := [...]int{1, 2, 3}
for i, ele := range arr { //ele是arr中元素的拷贝
    arr[i] += 8 //修改arr里的元素,不影响ele
    fmt.Printf("%d %d %d\n", i, arr[i], ele)
    ele += 1 //修改ele不影响arr
    fmt.Printf("%d %d %d\n", i, arr[i], ele)
}
for i := 0; i < len(arr); i++ {
    fmt.Printf("%d %d\n", i, arr[i])
}

cap和len

  • 在数组上调用cap()函数表示capacity容量,即给数组分配的内存空间可以容纳多少个元素;
  • len()函数代表length长度,即目前数组里有几个元素。
  • 由于数组初始化之后长度不会改变,不需要给它预留内存空间,所以len(arr)==cap(arr)。对于多维数组,其cap和len指第一维的长度。

数组传参

  • 数组的长度和类型都是数组类型的一部分,函数传递数组类型时这两部分都必须吻合。
  • go语言没有按引用传参,全都是按值传参,即传递数组实际上传的是数组的拷贝,当数组的长度很大时,仅传参开销都很大。
  • 如果想修改函数外部的数组,就把它的指针(数组在内存里的地址)传进来。

拷贝的发生

  1. 通过“=”进行赋值
  2. 函数(func)的入参
  3. 函数(func)的返回值
// 1. 通过“=”进行赋值
func go_copy() {
	a := 8
	b := a                        //=发生了拷贝
	fmt.Printf("%p %p\n", &a, &b) //0xc000122058 0xc000122070

	arr := [...]int{4, 8, 5}
	brr := arr
	fmt.Printf("%p %p\n", &arr, &brr) //pointer address //0xc000126078 0xc000126090
	brr[1] *= 2
	fmt.Println(arr[1], brr[1]) //8   16

}

// 2. 函数的入参,拷贝
func change_array(arr [3]int) { //*[3]int 指针类型。arr是指针
	fmt.Printf("%p\n", &arr) //0xc000010180,arr是个int。&arr是地址的地址
	//(*arr)[0]*=2 //指针解析;取出地址里的值。*arr是数组
	arr[0] *= 2 //隐式的把指针当成了数组来使用
}

// 3. 函数(func)的出参(返回值)
func create_array() [3]int {
	arr := [3]int{}
	fmt.Printf("%p\n", &arr)
	return arr

}

func main() {
	//init_array()
	//travese()
	//go_copy()
	arr := [3]int{5}         //5 0 0
	fmt.Printf("%p\n", &arr) //0xc000010198
	change_array(arr)
	//change_array(&arr)  //拷贝是谁?地址
	fmt.Println(arr[0]) //5
	//arr := create_array()
	//brr := create_array()
	//fmt.Printf("%p %p\n", &arr, &brr)
	//0xc000010198
	//0xc0000101c8
	//0xc000010180 0xc0000101b0
	//arr[0] = 9
	//fmt.Println(arr[0], brr[0]) //9   0
	//brr[0] = 3
	//fmt.Println(arr[0], brr[0]) //9   3
}
//参数必须是长度为5的int型数组(注意长度必须是5)
func update_array1(arr [5]int) {
	fmt.Printf("array in function, address is %p\n", &arr[0])
	arr[0] = 888

}

func update_array2(arr *[5]int) {
	fmt.Printf("array in function, address is %p\n", &((*arr)[0]))
	arr[0] = 888 //因为传的是数组指针,所以直接在原来的内存空间上进行修改
}

4. 切片

  切片是一个结构体,包含三个成员变量,array指向一块连续的内存空间,cap表示这块内存的大小,len表示目前该内存里存储了多少元素。

type slice struct { 
    array unsafe.Pointer 
    len int 
    cap int 
}

在这里插入图片描述
切片的初始化

var s []int //切片声明,len=cap=0
s = []int{} //初始化,len=cap=0
s = make([]int, 3) //初始化,len=cap=3
s = make([]int, 3, 5) //初始化,len=3,cap=5
s = []int{1, 2, 3, 4, 5} //初始化,len=cap=5
s2d := [][]int{
    {1},{2, 3}, //二维数组各行的列数相等,但二维切片各行的len可以不等
}

append

  切片相对于数组最大的特点就是可以追加元素,可以自动扩容。追加的元素放到预留的内存空间里,同时len加1。如果预留空间已用完,则会重新申请一块更大的内存空间,capacity大约变成之前的2倍(cap<1024)或1.25倍(cap>1024)。把原内存空间的数据拷贝过来,在新内存空间上执行append操作。

s := make([]int, 3, 5)
for i := 0; i < 3; i++ {
    s[i] = i + 1
} //s=[1,2,3]
fmt.Printf("s[0] address %p, s=%v\n", &s[0], s)
/*
capacity还够用,直接把追加的元素放到预留的内存空间上
*/
s = append(s, 4, 5) //可以一次append多个元素
fmt.Printf("s[0] address %p, s=%v\n", &s[0], s)
/*
capacity不够用了,得申请一片新的内存,把老数据先拷贝过来,在新内存上执行append操作
*/
s = append(s, 6)
fmt.Printf("s[0] address %p, s=%v\n", &s[0], s)
//探究capacity扩容规律
func expansion() {
	s := make([]int, 0, 3)
	prevCap := cap(s)
	for i := 0; i < 100; i++ {
		s = append(s, i)
		currCap := cap(s)
		if currCap > prevCap {
			//每次扩容都是扩到原先的2倍
			fmt.Printf("capacity从%d变成%d\n", prevCap, currCap)
			prevCap = currCap
		}
	}
}
arr := make([]int, 3, 5)
brr := append(arr, 8) //arr和brr共享底层数组,但它们的len不同

在这里插入图片描述

截取子切片

  通过指定起止下标,可以从大切片中截取一个子切片。

s := make([]int, 3, 5)	//len=3, cap=5
sub_slice = s[1:3]		//len=2, cap=4

刚开始,子切片和母切片共享底层的内存空间,修改子切片会反映到母切片上,在子切片上执行append会把新元素放到母切片预留的内存空间上。

当子切片不断执行append,耗完了母切片预留的内存空间,子切片跟母切片就会发生内存分离,此后两个切片没有任何关系。

在这里插入图片描述

func sub_slice() {
	/*
	截取一部分,创造子切片,此时子切片与母切片(或母数组)共享底层内存空间,母切片的capacity子切片可能直接用
	*/
	s := make([]int, 3, 5)
	for i := 0; i < 3; i++ {
		s[i] = i + 1
	} //s=[1,2,3]
	fmt.Printf("s[1] address %p\n", &s[1])
	sub_slice := s[1:3] //从切片创造子切片,len=2,cap=4
	fmt.Printf("len %d cap %d\n", len(sub_slice), cap(sub_slice))
	/*
	母切片的capacity还允许子切片执行append操作
	*/
	sub_slice = append(sub_slice, 6, 7) //可以一次append多个元素
	sub_slice[0] = 8
	fmt.Printf("s=%v, sub_slice=%v, s[1] address %p, sub_slice[0] address %p\n", s, sub_slice, &s[1], &sub_slice[0])
	/*
	母切片的capacity用完了,子切片再执行append就得申请一片新的内存,把老数据先拷贝过来,在新内存上执行append操作。此时的append操作跟母切片没有任何关系
	*/
	sub_slice = append(sub_slice, 8)
	sub_slice[0] = 9
	fmt.Printf("s=%v, sub_slice=%v, s[1] address %p, sub_slice[0] address %p\n", s, sub_slice, &s[1], &sub_slice[0])

	arr := [5]int{1, 2, 3, 4, 5}
	fmt.Printf("arr[1] address %p\n", &arr[1])
	sub_slice = arr[1:3] //从数组创造子切片,len=cap=2
	fmt.Printf("len %d cap %d\n", len(sub_slice), cap(sub_slice))
	/*
	母数组的capacity还允许子切片执行append操作
	*/
	sub_slice = append(sub_slice, 6, 7) //可以一次append多个元素
	sub_slice[0] = 8
	fmt.Printf("arr=%v, sub_slice=%v, arr[1] address %p, sub_slice[0] address %p\n", arr, sub_slice, &arr[1], &sub_slice[0])
	/*
	母数组的capacity用完了,子切片再执行append就得申请一片新的内存,把老数据先拷贝过来,在新内存上执行append操作。此时的append操作跟母数组没有任何关系
	*/
	sub_slice = append(sub_slice, 8)
	sub_slice[0] = 9
	fmt.Printf("arr=%v, sub_slice=%v, arr[1] address %p, sub_slice[0] address %p\n", arr, sub_slice, &arr[1], &sub_slice[0])
}

切片传参

  • go语言函数传参,传的都是值,即传切片会把切片的{arrayPointer, len, cap}这3个字段拷贝一份传进来。
  • 由于传的是底层数组的指针,所以可以直接修改底层数组里的元素。
func update_slice(s []int) {
	s[0] = 888
}
s := []int{1, 2, 3}
update_slice(s)
fmt.Printf("s=%v\n", s)
s := make([]int, 2, 3)
fmt.Printf("address of slice %p, address of array %p %p\n", &s, &s[0], s)
s = append(s, 4)
fmt.Printf("address of slice %p, address of array %p %p\n", &s, &s[0], s)
s = append(s, 4)
fmt.Printf("address of slice %p, address of array %p %p\n", &s, &s[0], s)

  获取切片的地址用&s;获取切片底层数组的地址用&s[0],或直接把s当地址打印。

5. 字符串

  字符串里可以包含任意Unicode字符。

s := " My name is 张三☻" 

  字符串里可以包含转义字符。

s := "He say:\"I'm fine.\" \n\\Thank\tyou.\\" 

  字符串也可以用反引号来定义,反引号里的转义字符无效。反引号里的内容原封不动地输出,包括空白符和换行符。

s := `here is first line. 

  there is third line.
`
func string_demo() {
	var s1 string
	var s2 string
	var s3 string

	s1 = "absdeABCz是中,☻\n" //string里的每一个字符是rune类型
	s2 = "hello \"china\" \n \\这是第二行\\\n"
	s3 = `<html>\n
    <head>我是标题<\head>
<\html>`

	fmt.Print(s1) //absdeABCz是中,☻
	fmt.Print(s2) //hello "china"
	// \这是第二行\
	fmt.Print(s3) //<html>\n
	//<head>我是标题<\head>
	//<\html>
}

a := 'H'                          //字符 byte
b := '中'                          //字符 rune
c := "H中"                         //字符串 string
fmt.Printf("%c %c %s\n", a, b, c) //H 中 H中
fmt.Printf("%d %d %d %d %d %d\n", 'a', 'b', 'c', 'z', 'A', 'B') //97 98 99 122 65 66

字符串常用操作

方法介绍
len(str)求长度
strings.Split分割
strings.Contains判断是否包含
strings.HasPrefix,strings.HasSuffix前缀/后缀判断
strings.Index(),strings.LastIndex()子串出现的位置
s := "born to win, born to die."
fmt.Printf("sentence length %d\n", len(s))
fmt.Printf("\"s\" length %d\n", len("s"))  //英文字母的长度为1
fmt.Printf("\"中\"  length %d\n", len("中")) //一个汉字占3个长度
arr := strings.Split(s, " ")
fmt.Printf("arr[3]=%s\n", arr[3])
fmt.Printf("contain die %t\n", strings.Contains(s, "die"))          //包含子串
fmt.Printf("contain wine %t\n", strings.Contains(s, "wine"))        //包含子串
fmt.Printf("first index of born %d\n", strings.Index(s, "born"))    //寻找子串第一次出现的位置
fmt.Printf("last index of born %d\n", strings.LastIndex(s, "born")) //寻找子串最后一次出现的位置
fmt.Printf("begin with born %t\n", strings.HasPrefix(s, "born"))    //以xxx开头
fmt.Printf("end with die. %t\n", strings.HasSuffix(s, "die."))      //以xxx结尾
s := "上"
bs := []byte(s)
fmt.Println(bs) //3个字节 [228 184 138]
fmt.Println(len(bs)) //3

var r rune                    //int32
r = '上'                      //占4个字节
fmt.Println(unsafe.Sizeof(r)) //4

func string_function_demo() {
	s := "abs,ABC,是中,☻\n" //一个rune占3个长度,一个byte占1个长度。8+4*3+1=21
	fmt.Println(len(s))   //等价于:把string转成byte切片后,切片的长度
	fmt.Println(strings.Split(s, ","))
	fmt.Println(strings.HasPrefix(s, "ab"))  //true
	fmt.Println(strings.HasPrefix(s, "abc")) //false
	fmt.Println(strings.HasSuffix(s, "☻"))   //false
	fmt.Println(strings.HasSuffix(s, "\n"))  //true
	fmt.Println(strings.Index(s, " "))       //-1
	fmt.Println(strings.LastIndex(s, " "))   //-1
	fmt.Println(strings.Index(s, ","))       //3
	fmt.Println(strings.LastIndex(s, ","))   //7
}

  把多个字符串拼接成一个长的字符串有多种方式。

  1. 加号连接。
  2. func fmt.Sprintf(format string, a …interface{}) string
  3. func strings.Join(elems []string, sep string) string
  4. 当有大量的string需要拼接时,用strings.Builder效率最高
s1 := "Hello"
s2 := "how"
s3 := "are"
s4 := "you"
merged := s1 + " " + s2 + " " + s3 + " " + s4
fmt.Println(merged)
merged = fmt.Sprintf("%s %s %s %s", s1, s2, s3, s4)
fmt.Println(merged)
merged = strings.Join([]string{s1, s2, s3, s4}, " ")
fmt.Println(merged)
//当有大量的string需要拼接时,用strings.Builder效率最高
sb := strings.Builder{}
sb.WriteString(s1)
sb.WriteString(" ")
sb.WriteString(s2)
sb.WriteString(" ")
sb.WriteString(s3)
sb.WriteString(" ")
sb.WriteString(s4)
sb.WriteString(" ")
merged = sb.String()
fmt.Println(merged)
func string_concate() {
	s1 := "hello"
	s2 := "golang"
	s3 := "\n"

	s4 := s1 + " " + s2 + " " + s3
	fmt.Print(s4) //hello golang
	s5 := fmt.Sprintf("%s %s %s", s1, s2, s3)
	fmt.Print(s5) //hello golang
	s6 := strings.Join([]string{s1, s2, s3}, " ")
	fmt.Print(s6) //hello golang

	sb := strings.Builder{}
	sb.WriteString(s1)
	sb.WriteString(" ")
	sb.WriteString(s2)
	sb.WriteString(" ")
	sb.WriteString(s3)
	s7 := sb.String()
	fmt.Print(s7) //hello golang

}

string中每个元素叫“字符”,字符有两种:

  1. byte:1个字节, 代表ASCII码的一个字符。
  2. rune:4个字节,代表一个UTF-8字符,一个汉字可用一个rune表示。

string是常量,不能修改其中的字符。

string可以转换为[]byte或[]rune类型。

s := "上A"
bs := []byte(s)
fmt.Println(bs) //[228 184 138 65]
rs := []rune(s)
fmt.Println(rs) //[19978 65]

fmt.Println(s[0])
//s[0] = 'b' //报错,常量不得的修改,指向到老内存中
s = "bA"  //指向到新内存中

string底层是byte数组,string的长度就是该byte数组的长度, UTF-8编码下一个汉字占3个byte,即一个汉字占3个长度。

s1 := "My name is 张朝阳"
arr := []byte(s1)
brr := []rune(s1)
fmt.Printf("last byte %d\n", arr[len(arr)-1]) //string可以转换为[]byte或[]rune类型
fmt.Printf("last byte %c\n", arr[len(arr)-1]) //byte或rune可以转为string
fmt.Printf("last rune %d\n", brr[len(brr)-1])
fmt.Printf("last rune %c\n", brr[len(brr)-1])
L := len(s1)
fmt.Printf("string len %d byte array len %d rune array len %d\n", L, len(arr), len(brr))
for _, ele := range s1 {
    fmt.Printf("%c ", ele) //string中的每个元素是字符
}
fmt.Println()
for i := 0; i < L; i++ {
    fmt.Printf("%c ", s1[i]) //[i]前面应该出现数组或切片,这里自动把string转成了[]byte(而不是[]rune)
}

6. 数据类型转换

  强制类型转换的基本方法就是把目标类型放在变量前面,把变量括起来。

var i int = 9
var by byte = byte(i)		//int转为byte
i = int(by)		//byte转为int
// 强制类型转换
func type_convert() {
	var i int = 300
	var b byte = byte(i)
	//fmt.Println(b) //44
	i = int(b)
	fmt.Println(i) //44

	//var i int = 200
	//var b byte = byte(i)
	//i = int(b)
	//fmt.Println(i) //200

	//无符号向有符号转换
	//var a uint8 = 255    //11111111
	//var b int8 = int8(a) //11111111
	//fmt.Println(b)       //-1
	1-->00000001   11111110   11111111

}
  • 低精度向高精度转换没问题,高精度向低精度转换会丢失位数。
  • 无符号向有符号转换,最高位是符号位。
  • byte和int可以互相转换。
  • float和int可以互相转换,小数位会丢失。
  • bool和int不能相互转换。
  • 不同长度的int或float之间可以相互转换。
  • 不能对常量进行强制类型转换
//高精度向低精度转换,数字很小时这种转换没问题
var ua uint64 = 1
i8 := int8(ua)
fmt.Printf("i8=%d\n", i8)

//最高位的1变成了符号位
ua = uint64(math.MaxUint64)
i64 := int64(ua)
fmt.Printf("i64=%d\n", i64)

//位数丢失
ui32 := uint32(ua)
fmt.Printf("ui32=%d\n", ui32)

//单个字符可以转为int
var i int = int('a')
fmt.Printf("i=%d\n", i)

//bool和int不能相互转换

//byte和int可以互相转换
var by byte = byte(i)
i = int(by)
fmt.Printf("i=%d\n", i)

//float和int可以互相转换,小数位会丢失
var ft float32 = float32(i)
i = int(ft)
fmt.Printf("i=%d\n", i)

string和其他数据类型互转。

var err error
var i int = 8
var i64 int64 = int64(i)
//int转string
var s string = strconv.Itoa(i) //内部调用FormatInt
s = strconv.FormatInt(i64, 10)
//string转int
i, err = strconv.Atoi(s)
//string转int64
i64, err = strconv.ParseInt(s, 10, 64)

//float转string
var f float64 = 8.123456789
s = strconv.FormatFloat(f, 'f', 2, 64) //保留2位小数
fmt.Println(s)
//string转float
f, err = strconv.ParseFloat(s, 64)

//string<-->[]byte
var arr []byte = []byte(s)
s = string(arr)

//string<-->[]rune
var brr []rune = []rune(s)
s = string(brr)

fmt.Printf("err %v\n", err)
func string_convert() {

	//line := "3.6546"
	//f64, err := strconv.ParseFloat(line, 64)
	//if err == nil { //转换成功
	//	fmt.Printf("%3f\n", f64)  //3.654600
	//	fmt.Printf("%.3f\n", f64) //3.655
	//}

	//line := "3.65A46"
	//f64, err := strconv.ParseFloat(line, 64)
	//if err == nil { //转换成功
	//	fmt.Printf("%.3f\n", f64)
	//} else { //转换失败
	//	fmt.Println(err) //strconv.ParseFloat: parsing "3.65A46": invalid syntax
	//}

	line := "3.6546"
	f64, err := strconv.ParseFloat(line, 64)
	if err == nil { //转换成功
		fmt.Printf("%.3f\n", f64) //3.655
		//s := strconv.FormatFloat(f64, 'f', 2, 64) //3.65
		//s := strconv.FormatFloat(f64, 'f', 4, 64) //3.6546
		//s := strconv.FormatFloat(f64, 'e', 4, 64) //3.6546e+00
		s := strconv.FormatFloat(f64, 'g', 4, 64) //3.655
		fmt.Println(s)
	} else { //转换失败
		fmt.Println(err)
	}

7. map

Hash Table

  go map的底层实现是hash table,根据key查找value的时间复杂度是O(1)。

在这里插入图片描述
map的初始化

var m map[string]int //声明map,指定key和value的数据类型
m = make(map[string]int) //初始化,容量为0
m = make(map[string]int, 200) //初始化,容量为5。强烈建议初始化时给一个合适的容量,减少扩容的概率
m = map[string]int{"语文": 0, "数学": 39} //初始化时直接赋值

添加和删除key

m["英语"] = 59 //往map里添加key-value对
m ["英语"] = 70 //会覆盖之前的值
delete(m, "数学") //从map里删除key-value对

  len(m)获取map的长度,go不支持对map上执行cap函数。
  读取key对应的value时,如果key不存在,则返回value类型的默认值,所以强烈建议先判断key是否存在。

if value, exists := m["语文"]; exists {
    fmt.Println(value)
} else {
    fmt.Println("map里不存在[语文]这个key")
}

遍历map

//遍历map
for key, value := range m {
    fmt.Printf("%s=%d\n", key, value)
}
fmt.Println("-----------")
//多次遍历map返回的顺序是不一样的,但相对顺序是一样的,因为每次随机选择一个开始位置,然后顺序遍历
for key, value := range m {
    fmt.Printf("%s=%d\n", key, value)
}
fmt.Println("-----------")

//一边遍历一边修改
for key, value := range m {
    m[key] = value + 1
}
for key, value := range m {
    fmt.Printf("%s=%d\n", key, value)
}
fmt.Println("-----------")

//for range取得的是值拷贝
for _, value := range m {
    value = value + 1
}
for key, value := range m {
    fmt.Printf("%s=%d\n", key, value)
}

  map中的key可以是任意能够用==操作符比较的类型,不能是函数、map、切片,以及包含上述3中类型成员变量的的struct。map的value可以是任意类型。

type f func(int) bool
type m map[int]byte
type s []int

type i int

var m1 map[i]f
fmt.Println(m1)

/** 函数、map、切片不能当key **/
// var m2 map[f]bool
// fmt.Println(m2)
// var m3 map[m]bool
// fmt.Println(m3)
// var m4 map[s]bool
// fmt.Println(m4)

type user struct {
	scores float32 //如果scores是slice,则user不能作为map的key
}

u := user{}
m5 := make(map[user]interface{})
m5[u] = 5
fmt.Println(m5)
package main

import (
	"fmt"
	"strconv"
)

func main() {
	var m map[int]bool
	m = map[int]bool{4: true, 2: false, 200: true}
	m[32] = false
	m[32] = true
	fmt.Println(m[32])  //true
	fmt.Println(len(m)) //4

	delete(m, 32)
	fmt.Println(len(m)) //3
	//fmt.Println(m[32]) //false
	//fmt.Println(m[2])  //false

	if v, exists := m[32]; exists {
		fmt.Println("32对就的value是" + strconv.FormatBool(v))
	} else {
		fmt.Println("map里不存在32这个key")
	}

	if v, exists := m[2]; exists {
		fmt.Println("2对就的value是" + strconv.FormatBool(v))
	} else {
		fmt.Println("map里不存在2这个key")
	}
	//true
	//map里不存在32这个key
	//2对就的value是false

	for k, v := range m {
		fmt.Printf("%d=%t\n", k, v) //4=true 2=false 200=true

	}
}

8. channel

channel(管道)底层是一个环形队列(先进先出),send(插入)和recv(取走)从同一个位置沿同一个方向顺序执行。
sendx表示最后一次插入元素的位置,recvx表示最后一次取走元素的位置。

在这里插入图片描述

管道的声明和初始化

var ch chan int //管道的声明
ch = make(chan int, 8) //管道的初始化,环形队列里可容纳8个int

send和revc

ch <- 1                //往管道里写入(send)数据
ch <- 2
ch <- 3
ch <- 4
ch <- 5
v := <-ch //从管道里取走(recv)数据
fmt.Println(v)
v = <-ch
fmt.Println(v)

只读/只写channel

read_only := make (<-chan int)   //定义只读的channel
write_only := make (chan<- int)   //定义只写的channel

定义只读和只写的channel意义不大,一般用于在参数传递中。

//只能向channel里写数据 
func send(c chan<- int) { 
    c <- 1 
} 

//只能取channel中的数据 
func recv(c <-chan int) {
	_ = <-c
}

//返回一个只读channel
func (c *Context) Done() <-chan struct{} {
    return nil
}

遍历管道

  可以通过for range遍历并取走管道里的元素,当管道为空且被close后,for循环退出。

close(ch)
//遍历并取走(receive)管道里的元素。当管道里已无剩余元素且没有close管道时,receive操作会一直阻塞,最终报deadlock
for ele := range ch {
	fmt.Println(ele)
}

引用类型

  • slice、map和channel是go语言里的3种引用类型,都可以通过make函数来进行初始化(申请内存分配)。
  • 因为它们都包含一个指向底层数据结构的指针,所以称之为“引用”类型。
  • 引用类型未初始化时都是nil,可以对它们执行len()函数,返回0。

作业:

1、定义一个5行4列的float数组,把里面的元素全部放到一个一维切片里
2、arr:=make([]int,3,4);brr:=append(arr,1)问arr里的元素是什么?arr和brr会相互影响么?写代码验证一下
3、创建一个初始长度为0、容量为10的int型切片,调用rand.Intn(128)100次,往切片里面添加100个元素,利用map统计该切片里有多少个互不相同的元素
4、实现一个函数func arr2string(arr []int)string,比如输入[]int{2,4,6},返回“2 4 6”。输入的切片可能很短,也可能很长。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值