Go 基础数据结构与流程控制

Go 命令
	-go build main.go
	-go run main.go
	-go install main.go
Go语言基础语法
	-文件第一行:packae关键字声明包名
	-如果要编译可执行文件,必须要有main包合main函数(入口函数),它没有参数也没有返回值
	-单行注释 //
	-多行注释 /*  */
	
Go语言函数外的语句必须以关键字开头

函数内部定义的变量必须使用

变量

3种声明方式:

  1. var name1 string
  2. var name2 = "hina"
  3. 函数的内部专属:name3 := "hina"

匿名变量(哑元变量):

当有些数据必须用变量接收但是又不是用它时,就可以用_来接收这个值

常量

const pi = 3.1415926

const UserNotExistErr = 10000

iota:实现枚举

两个要点:

  1. iota在const关键字出现时将被重置为0
  2. const中每新增一行常量声明,iota累加1

流程控制

if 判断

var age = 19
	if age>18{
		fmt.Println("成年了")
	}else if age>7{
		fmt.Println("上过小学")
	}else{
		fmt.Println("快乐时光")
	}

for 循环

    for i := 0; i < 10; i++ {
        fmt.Println(i)
    }
	// 九九乘法表
	for i := 1; i <= 9; i++ {
		for j := 1; j <= i; j++ {
			fmt.Printf("%dx%d=%d\t", j, i, i*j)
		}
		fmt.Println()
	}

	// 当i=5时跳出for循环
	// for i := 0; i < 10; i++ {
	// 	if i == 5 {
	// 		break
	// 	}
	// 	fmt.Println(i)
	// }
	// fmt.Println("over")

	// 当i=5时跳过此次for循环
	for i := 0; i < 10; i++ {
		if i == 5 {
			continue
		}
		fmt.Println(i)
	}
	fmt.Println("over")

基本数据类型

整型

​ 无符号整型:uint8,uint16,uint32,uint64

​ 带符号整型:int8,int16,int32,int64

int:具体是32位还是64位看操作系统

uintptr:表示指针

// 定义二进制数
var n1 = 0b011
fmt.Println(n1)
fmt.Printf("%b", n1)

// 定义八进制数
var n2 = 011
fmt.Println(n2)
fmt.Printf("%o", n2)

// 定义十六进制数
var n3 = 0xf1
fmt.Println(n3)
fmt.Printf("%x", n3)

浮点型

float64float32

​ Go语言中浮点数默认是float64

复数

​ complex128和complex64

布尔值

truefalse

​ 不能和其他类型做转换

字符串

​ 常用方法

​ 字符串不能修改

byte和rune类型

​ 都属于类型别名

字符串、字符、字节都是什么?

​ 字符串:双引号包裹的是字符串

​ 字符:单引号包裹的是字符,单个字母、符号、文字

​ 字节:1byte=8bit

​ Go语言中字符串都是UTF-8编码

今日内容

for循环

package main

import (
	"fmt"
)

// switch 简化大量的判断(一个变量和具体的值作比较)

func main() {
	// var n = 8
	// if n == 1 {
	// 	fmt.Println("x1")
	// } else if n == 2 {
	// 	fmt.Println("x2")
	// } else if n == 3 {
	// 	fmt.Println("x3")
	// } else if n == 4 {
	// 	fmt.Println("x4")
	// } else if n == 5 {
	// 	fmt.Println("x5")
	// } else {
	// 	fmt.Println("nowork")
	// }

	// switch简化上面的代码
	// switch n := 3; n {
	// case 1:
	// 	fmt.Println("x1")
	// case 2:
	// 	fmt.Println("x2")
	// case 3:
	// 	fmt.Println("x3")
	// case 4:
	// 	fmt.Println("x4")
	// case 5:
	// 	fmt.Println("x5")
	// default:
	// 	fmt.Println("nowork")
	// }

	// switch n := 7; n {
	// case 1, 3, 5, 7, 9:
	// 	fmt.Println("奇数")
	// case 2, 4, 6, 8, 0:
	// 	fmt.Println("偶数")
	// default:
	// 	fmt.Println(n)
	// }

	// age := 30
	// switch {
	// case age < 18:
	// 	fmt.Print("未成年")
	// case age >= 18 && age <= 30:
	// 	fmt.Println("青年")
	// case age > 35:
	// 	fmt.Println("中年")
	// default:
	// 	fmt.Println("其他")
	// }

	// fallthrough
	// s := "a"
	// switch {
	// case s == "a":
	// 	fmt.Println("yes")
	// 	fallthrough
	// case s == "b":
	// 	fmt.Println("bno")
	// default:
	// 	fmt.Println("...")
	// }

	// var flag = false
	// for i := 0; i < 10; i++ {
	// 	for j := 'A'; j < 'Z'; j++ {
	// 		if j == 'C' {
	// 			// flag = true
	// 			i = 10
	// 			break // 跳出内层for循环
	// 		}
	// 		fmt.Printf("%v-%c\n", i, j)
	// 	}
	// if flag {
	// 	break // 跳出外层for循环
	// }
	// }

	// goto + label 实现跳出for循环
	for i := 0; i < 10; i++ {
		for j := 'A'; j < 'Z'; j++ {
			if j == 'C' {
				goto Flag // 跳到指定的标签
			}
			fmt.Printf("%v-%c\n", i, j)
		}
	}
Flag: // label 标签
	fmt.Println("over")
}

运算符

package main

import (
	"fmt"
)

// 运算符

func main() {
	a := 5
	b := 2

	a++ // 单独的语句 a = a + 1
	b-- // b = b - 1

	// 关系运算符
	fmt.Println(a == b) // Go语言是强类型,相同类型的变量才能比较

	// age := 30
	// if age > 18 && age < 60 {
	// 	fmt.Println("青年")
	// } else {
	// 	fmt.Println("非人哉")
	// }

	// 逻辑运算符
	// &&:且,||:或者, !:取反,

	// 位运算符 针对的是二进制数
	// 5的二进制数表示: 101
	// 2的二进制数表示: 010

	// &:按位与(二进制数两个都为1才为1)
	fmt.Println(5 & 2) // 000
	// |:按位或(二进制数两个有一个为1就为1)
	fmt.Println(5 | 2) // 111
	// ^:按位异或(二进制数不一样则为1)
	fmt.Println(5 ^ 2) // 111
	// <<:将二进制位左移指定位数
	fmt.Println(5 << 1)  // 1010
	fmt.Println(1 << 10) // 10000000000 => 1024
	fmt.Println(5 >> 1)  // 10

	// var m = int8(1)  // 只能存8位二进制数
	// fmt.Printf(m << 10)

	// 赋值运算符,用来给变量赋值的
	x := 10
	x <<= 2 // x = x << 2
	x &= 2  // x = x & 2
	x |= 2  // x = x | 2
	x ^= 2  // x = x ^ 2
	x >>= 2 // x = x >> 2

}

复合数据类型

数组 Array

package main

import "fmt"

// 复合数据类型

// 数组
// 存放元素的容器,必须指定存放的元素的类型和容量(长度)

func main() {
	// 数组的长度时数组类型的一部分,长度不一样不可比较
	// var a1 [3]bool // [true false true]
	// var a2 [4]bool // [true false true false]

	// fmt.Printf("a1:%T, a2:%T\n", a1, a2)

	// // 数组的初始化
	// // 如果不出是花:默认元素都是零值(布尔值:false,整型和浮点型都是0,字符串:"")
	// fmt.Println(a1, a2)
	// // 1.初始化方法1
	// a1 = [3]bool{true, true, true}
	// fmt.Println(a1)
	// // 2.初始化方法2:根据初始值自动退管数组的长度时多少
	// ax := [...]int{1, 2, 3, 4, 5, 6, 7}
	// fmt.Println(ax)
	// // 3.初始化方式3:根据索引来初始化
	// a3 := [5]int{0: 1, 4: 2}
	// fmt.Println(a3)

	// 数组的遍历
	// cities := [...]string{"BJ", "SH", "SZ"}
	// // 1.根据所有遍历
	// for i := 0; i < len(cities); i++ {
	// 	fmt.Println(cities[i])
	// }
	// // 2.for range 遍历
	// for i, v := range cities {
	// 	fmt.Println(i, v)
	// }

	// 多维数组 [ [1 2] [3 4] [5 6]]
	// var a11 [3][2]int
	// a11 := [3][2]int{
	// 	{1, 2},
	// 	{3, 4},
	// 	{5, 6},
	// }
	// fmt.Println(a11)

	// 多维数组的遍历
	// for _, v1 := range a11 {
	// 	fmt.Println(v1)
	// 	for _, v2 := range v1 {
	// 		fmt.Println(v2)
	// 	}
	// }

	// 数组是值类型
	// b1 := [3]int{1, 2, 3}
	// b2 := b1  // 深拷贝
	// b2[0] = 100
	// fmt.Println(b1, b2)

	// 练习求数组[1, 3, 5, 6, 8]所有元素的和
	arr := [...]int{1, 3, 5, 7, 8}
	// sum := 0
	// for _, v := range arr {
	// 	sum += v
	// }
	// fmt.Println(sum)

	// 找出数组中和为指定值得两个元素的下标,如从[1,3,5,7,8]找出和为8的两个元素的下标
	for i, v := range arr {
		for j := i + 1; j < len(arr)-i; j++ {
			if v+arr[j] == 8 {
				fmt.Println(i, j)
			}
		}	
	}
}

切片

切片是指向了一个底层的数组

切片的长度就是它元素的个数

切片的容量是底层数组从切片的第一个元素到最后一个元素的数量

切片与数组的区别

数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值。在初始化后长度是固定的,无法修改其长度。当作为方法的参数传入时将复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分,通过内置函数len(array)获取其长度。

数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型Slices切片(“动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。切片中有两个概念:一是len长度,二是cap容量,长度是指已经被赋过值的最大下标+1,可通过内置函数len()获得。容量是指切片目前可容纳的最多元素个数,可通过内置函数cap()获得。切片是引用类型,因此在当传递切片时将引用同一指针,修改值将会影响其他的对象。 

切片的本质

切片的本质就是一个框,框住了一块连续的内存。

切片属于引用类型,真正的数据都是保存在底层数组里的。

要判断一个切片是否为空的,要用len(s)==0来判断

package main

import "fmt"

// 切片slice

func main() {
	// 切片的定义
	// var s1 []int    // 定义一个存放int类型元素的切片
	// var s2 []string // 定义了一个存放string类型元素的切片
	// fmt.Println(s1, s2)
	// fmt.Println(s1 == nil) // true nil 表示没有开辟内存空间
	// fmt.Println(s2 == nil)

	// 初始化
	// s1 = []int{1, 2, 3}
	// s2 = []string{"hina", "lem", "阳菜"}
	// fmt.Println(s1, s2)
	// fmt.Println(s1 == nil)
	// fmt.Println(s2 == nil)

	// 长度和容量
	// fmt.Printf("len(s1):%d cap(s1):%d\n", len(s1), cap(s1))
	// fmt.Printf("len(s2):%d cap(s2):%d\n", len(s2), cap(s2))

	// // 2.由数组得到切片
	// a1 := [...]int{1, 3, 4, 5, 6, 7}
	// // s3 := a1[1:4]
	// // fmt.Println(s3)

	// s5 := a1[:4] // [1 3 4 5]
	// s6 := a1[4:] // [6 7]
	// fmt.Println(s5)
	// fmt.Println(s6)
	// fmt.Println(a1)
	// // fmt.Println(a1[1:])

	// // 切片的容量是指底层数组的容量
	// fmt.Printf("len(s5):%d cap(s5):%d\n", len(s5), cap(s5)) // 4, 6
	// // 底层数组从切片的第一个元素到最后的元素数量
	// fmt.Printf("len(s6):%d cap(s6):%d\n", len(s6), cap(s6)) // 2, 2
	// // 切片再切片
	// s8 := s5[2:]                                            // [4 5]
	// fmt.Printf("len(s8):%d cap(s8):%d\n", len(s8), cap(s8)) // 2, 2

	// 切片是引用类型,都是指向了底层的一个数组
	// fmt.Println("s6", s6)
	// a1[4] = 111 // 修改了底层数组的值
	// a1[5] = 222
	// a1[2] = 333
	// fmt.Println("s6", s6)
	// fmt.Println("s5", s5)

	// 使用make()函数创建切片

	// ss1 := make([]int, 5, 10) // 5是长度,10是容量,不写10默认和长度一样
	// fmt.Println(ss1, len(ss1), cap(ss1))

	// ss2 := make([]int, 0, 10) // 5是长度,10是容量,不写10默认和长度一样
	// fmt.Println(ss2, len(ss2), cap(ss2))

	// 切片的赋值
	s3 := []int{1, 3, 5}
	s4 := s3 // s3和s4都指向了用一个底层数组,切片的本质就是一个框
	fmt.Println(s3, s4)
	s3[0] = 1000
	fmt.Println(s3, s4)

	// 切片的遍历
	// 1.索引遍历
	for i := 0; i < len(s3); i++ {
		fmt.Println(s3[i])
	}

	// 2.for range循环
	for i, v := range s3 {
		fmt.Println(i, v)
	}
}

切片的扩容

package main

import "fmt"

// append()为切片追加元素

func main() {
	// var a int = 10
	// //每个变量都有两层含义,变量的内存和变量的地址
	// fmt.Printf("a = %d\n", a)  //变量的内存 10
	// fmt.Printf("a = %v\n", &a) //变量的地址 0xc042060080

	s1 := []string{"lem", "hina", "rui"}
	fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))

	// 调用append函数必须用原来的变量来接收返回值
	s1 = append(s1, "emt") // append追加元素,原来的底层数组放不下时,Go底层就会换一个内存地址去存数据
	fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))

	s1 = append(s1, "xx", "yy")
	fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))

	ss := []string{"aa", "bb", "cc"}
	s1 = append(s1, ss...)
	fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))

}

append

package main

import (
	"fmt"
)

// copy

func main() {
	ia := [...]int{1, 2, 3, 4, 5}
	ia2 := ia[1:3]
	ia[2] = 1000
	fmt.Printf("%p, %v\n", &ia, ia)
	ia2 = append(ia2, 7, 8)
	fmt.Printf("%p, %v\n", &ia, ia)
	fmt.Printf("%p, %v\n", &ia2, ia2)

	fmt.Println("===============================")
	a := [...]int{1, 2, 3, 4, 5}
	a2 := a[1:3]
	fmt.Printf("%p, %v\n", &a, a)
	a2 = append(a2, 7, 8, 9, 10)
	fmt.Printf("%p, %v\n", &a, a)
	fmt.Printf("%p, %v\n", &a2, a2)
}
>>>>>>>>
0xc00000c360, [1 2 1000 4 5]
0xc00000c360, [1 2 1000 7 8]
0xc000004078, [2 1000 7 8]
===============================
0xc00000c3f0, [1 2 3 4 5]
0xc00000c3f0, [1 2 3 4 5]
0xc0000040a8, [2 3 7 8 9 10]

copy

package main

import (
	"fmt"
	"sort"
)

// copy

func main() {
	// a1 := []int{1, 3, 5}
	// a2 := a1 // 赋值 a1,a2指向的是同一个内存地址
	// // var a3 []int  // nil没有内存空间
	// var a3 = make([]int, 3, 3)
	// copy(a3, a1) // copy  a3是新开辟一块内存将原来的数据复制过来
	// fmt.Println(a1, a2, a3)

	// a1[0] = 100
	// fmt.Println(a1, a2, a3)

	// // 将a1中索引为1的3这个元素删除
	// a1 = append(a1[:1], a1[2:]...)
	// fmt.Println(a1, cap(a1))

	// x1 := [...]int{1, 3, 5} // 数组
	// s1 := x1[:]             // 切片
	// fmt.Println(s1, len(s1), cap(s1))

	// 1.切片不保存具体的值
	// 2.切片对应一个底层数据
	// 3.底层数组都是占用一块连续的内存
	// s1 = append(s1[:1], s1[2:]...)
	// fmt.Println(s1, len(s1), cap(s1))
	// fmt.Println(x1)

	// 切片的练习
	var a = make([]int, 5, 10) // 创建切片 长度为5,容量为10
	fmt.Println(a)
	for i := 0; i < 10; i++ {
		a = append(a, i)
	}
	fmt.Println(a)

	a1 := [...]int{3, 5, 7, 1, 2}
	sort.Ints(a1[:]) // 对切片进行排序
	fmt.Println(a1)
}

指针

Go语言中不存在指针操作,只需要记住两个符号

  1. &:取地址
  2. *:根据地址取值
package main

import (
	"fmt"
)

// 指针

func main() {
	// 1.&:取地址

	// n := 18
	// p := &n
	// fmt.Println(p)
	// fmt.Printf("%T\n", p) // *int表示int类型的指针

	// // 2.*:根据变量取值
	// m := *p
	// fmt.Println(m)
}

new函数

package main

import (
	"fmt"
)

// 指针

func main() {
	var a *int // nil 空指针
	fmt.Println(a)
	// *a = 100   // 空的指针是找不到值得
	// fmt.Println(*a)

	// new函数申请一个内存地址
	var a1 = new(int)
	fmt.Println(*a1)
	*a1 = 100
	fmt.Println(a1)
	fmt.Println(*a1)

}
>>>>>>>>
<nil>
0
0xc0000140e0
100

new和make的区别

  1. make和new都是用来申请内存的
  2. new很少用,一般用来给基本数据类型申请内存,stringint,返回的是对于类型的指针
  3. make是用来给slice,map,channel申请内存的,make函数返回的是对于的者三个类型本身

map

package main

import (
	"fmt"
)

// map

func main() {

	var m1 map[string]int
	fmt.Println(m1)               // 还没有初始化,还没有在内存中开辟空间
	m1 = make(map[string]int, 10) // 要估算号该map容量,避免在程序运训期间再动态扩容
	fmt.Println(m1)
	m1["hina"] = 1111
	m1["lem"] = 2222
	m1["lem1"] = 22221

	fmt.Println(m1)
	fmt.Println(m1["hina"])
	fmt.Println(m1["hinaxx"]) // 如果不存在这个key拿到对应类型的0值

	val, ok := m1["xxx"]
	if !ok {
		fmt.Println("查无此key")
	} else {
		fmt.Println(val)
	}

	// map的遍历
	for k, v := range m1 {
		fmt.Println(k, v)
	}

	// 只遍历key
	for k := range m1 {
		fmt.Println(k)
	}

	// 只遍历value
	for _, v := range m1 {
		fmt.Println(v)
	}

	// 删除
	delete(m1, "lem1")
	fmt.Println(m1)
	// 删除不存在的key,则不操作
}

对于map类型中按照key排序在输出

package main

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

// map

func main() {

	rand.Seed(time.Now().UnixNano()) // 初始化随机数种子

	var scoremap = make(map[string]int, 200)

	for i := 0; i < 100; i++ {
		key := fmt.Sprintf("stu%02d", i) // 生成stu开头的字符串
		value := rand.Intn(100)          // 生成0-99随即证书
		scoremap[key] = value
	}

	fmt.Println(scoremap)
	// 取出map中所有key存入切片sl
	s1 := make([]string, 0, 200)
	for key := range scoremap {
		s1 = append(s1, key)
	}
	fmt.Println(s1)
	// 对切片进行排序
	sort.Strings(s1)
	fmt.Println(s1)
	// 按照排序后的key遍历map
	for _, key := range s1 {
		fmt.Println(key, scoremap[key])
	}

}

map和slice组合

package main

import "fmt"

// map和slice组合
// 切记不管是哪种组合都需要初始化两个类型

func main() {

	// 元素类型为map的切片
	var s1 = make([]map[int]string, 10, 10)
	// s1[0][100] = "A"  // 没有对内部的map做初始化

	s1[0] = make(map[int]string, 10)
	s1[0][0] = "HINA"
	fmt.Println(s1)

	// 值为切片类型的map
	var m1 = make(map[string][]int, 10)
	m1["北京"] = []int{10, 20, 30}
	fmt.Println(m1)
}

函数

package main

import (
	"fmt"
)

// 函数

// 函数的定义
func sum(x int, y int) (res int) {
	return x + y
}

// 函数没有返回值
func f1(x int, y int) {
	fmt.Println(x + y)
}

// 没有参数没有返回值
func f2() {
	fmt.Println("f2")
}

// 没有参数有返回值
func f3() int {
	return 111
}

// 返回值可以命名也可以不命名
// 命名的返回值就相当于在函数中声明一个变量
func f4(x int, y int) (ret int) {
	ret = x + y
	return // 使用命名返回值return后面可以省略
}

// 多个返回值
func f5() (int, string) {
	return 11, "HINA"
}

// 参数的类型简写:当参数中连续多个参数类型一致时,我们可以将前面那个参数的类型省略
func f6(x, y int) int {
	return x + y
}

// 可变长参数,必须放在函数参数最后
func f7(x string, y ...int) {
	fmt.Println(x, y) // y的类型是切片 []int
}

// Go语言中没有默认参数这个概念

func main() {
	res := sum(10, 20)
	fmt.Println(res)

	_, n := f5()
	fmt.Println(n)

	f7("hina")
	f7("hina", 1, 2, 3, 4, 6)
}

posted @ 2021-05-10 10:47  橘丶阳菜  阅读(73)  评论(0编辑  收藏  举报