Welcome to kimi's blog

GO

GO

Go语言介绍

Go 即Golang,是Google公司2009年11月正式对外公开的一门编程语言,Go是静态强类型语言,是区别于解析型语言的编译型语言(静态:类型固定 强类型:不同类型不允许直接运算)。

解析型语言:源代码有解析器对代码进行解释执行,类似于python、nodejs、php

编程型语言:源代码编译生成机器语言,然后有机器直接执行机器码即可,类似c \ c++ \ java \ c# \ go

# java
     有编译过程,没有直接编译成机器语言,而是字节码,机器不能识别,但java虚拟机:jvm能识别
      运行java必须要有jvm
      jvm:java虚拟机
      jre:java运行时环境
      jdk:java开发环境
        
#python
    python是可执行文件,打包python项目或flask项目需要借助pipinstaller模块,详细前往:https://zhuanlan.zhihu.com/p/430490285

如何防止python代码泄露?

  1. 项目启动后,把源代码删除了,但项目停了就需要重新部署,较繁琐
  2. 借助pipinstaller,将代码打包成可执行文件
  3. 做到docker镜像中,运行容器,授予授权码(-e password)

Go语言特性

  1. 跨平台,是编译性语言
  2. 语法接近c语言
  3. 管道(channel),切片(slice),并发(routine)
  4. 有垃圾回收的机制
  5. 支持面向对象和面向过程的编程模式

Go语言发展

2009年谷歌公司推出的

2015年8月19日 go1.5 —— 实现的架构变化,同时保留了和旧版本的兼容性,本次更新中移除了”最后残余的C代码”---》以后再写go不用c写了,用go写---》语言的自举

2018年8月24日 go1.11 —— modules和WebAssembly支持 go mod模式 包管理

最新版是 go1.20

Go语言应用

Go语言写的:k8s、docker、七牛( 许式伟,七牛云创始人兼*CEO *)

应用领域

  1. 服务开发、并发、分布式系统、微服务等方向,自动化运维,区块链

  2. 内存KV数据库,例如boltDB、levelDB

  3. 云平台

Go语言发展前景

Go语言将登上语言榜前列,与C和Java并列前三甲
 Go语言称霸云计算
 Go将统治下一个10年
 目前Go语言已经⼴泛应用于人工智能、云计算开发、容器虚拟化、⼤数据开发、数据分析及科学计算、运维开发、爬虫开发、游戏开发等领域。
 Go语言简单易学,天生支持并发,完美契合当下高并发的互联网生态

Go开发环境搭建

GO的语言搭建需要go的SDK和go的编辑器

go的SDK---》开发环境----》下载适合电脑go1.20.3.windows-amd64.msi

go的编辑器--》goland(jetbrains系列全家桶),vscode---goland-2023.1.exe

试用30天或破译
image

第一个helloworld

# 创建项目
# 创建一个go文件
# 写代码
package main // 声明这个go文件是属于哪个包
import "fmt" // 表示导入fmt包,自动导入的,goland自动导入的

func main() {
	fmt.Println("helloworld")

}


//解析
package main // 声明这个go文件属于哪个包,main有特殊含义,一个项目要运行必须要有main包,运行的入口是main包下的main函数

// 表示导入fmt包,自动导入的,goland自动导入的,使用了哪个包,编辑器会自动导入
// 包导入,不使用,会报错,编译不过
import "fmt"

// func 定义一个函数 ,main 是函数名,没有参数,没有返回值,整个项目入口
func main() {
	// 在控制台输出helloworld
	fmt.Println("helloworld")  // a... 提示的,意思是传的helloworld实参,给了a

}

// 编译代码---》编译成可执行文件---》默认编译成当前平台可执行文件---》支持跨平台编译---》想编译出linux的---》交叉编译

//编译  go build 文件名----》编译成可执行文件
// 运行(文件名.go),直接运行  双击

// 开发阶段----》编译并运行---》生成可执行文件,不再当前路径,用完就删除

// 编辑器有个快捷运行方式,右键运行

变量命名规范

Go语言中的函数名、变量名、常量名、类型名和包名等所有的命名,都遵循一个简单的命名规则:

  1. 一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线
  2. 大写字母和小写字母是不同的:Name和name是两个不同的变量(区分大小写,但是go的大写有特殊含义)
  3. 关键字和保留字都不建议用作变量名

关键字:25个,编辑器中会变色

break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

保留字:37个

内置常量: true false iota nil
内置类型:  int int8 int16 int32 int64
          uint uint8 uint16 uint32 uint64 uintptr
          float32 float64 complex128 complex64
          bool byte rune string error
内置函数: make len cap new append copy close delete
          complex real imag
          panic recover

总结

  1. 建议定义变量,函数名使用------》驼峰式命名方法
  2. go文件命名:建议使用下划线

变量的定义和使用

  1. 变量的完整定义

    // var关键字 变量名 变量类型  = 变量值
    	var name string = "kimi"
    	fmt.Println(name) // kimi
    	var age int = 19
    	fmt.Println(age)  //19
    
  2. 变量定义了,就必须使用,否则报错

    var name string = "kimi"
    //name declared and not used
    
  3. 类型推导(不写类型)---》自动推导出kiki是字符串类型

    var name = "kimi"
    var age = 19
    fmt.Println(name)  //kimi
    fmt.Println(age)  //19
    
  4. 虽然是类型推导出来的,但是是有类型的,并且后期不能变换类型

    变量只要定义了,类型就固定了,如变量name先定义为字符串,后期就能更改为int

    而python是动态强类型语言,是后期可以转换类型

  5. 简略声明,省略类型,必须使用 :=

    name := "kimi"
    fmt.Println(name) //kimi
    
  6. 一次性声明多个变量(其实就是基于上面三种变量定义方式的变形)

    1.完整定义
       //var name, age, gender string = "kimi", "18", "女"
    var (
    	name   string = "kiki"
    	age    int    = 18
    	gender string = "女"
    )
    fmt.Println(name, age, gender) // kimi 18 女
      
    2.类型推导
      //var name, age, gender = "kimi", 18, "女"
    var (
    	name   = "kiki"
    	age    = 18
    	gender = "女"
    )
    fmt.Println(name, age, gender) //kimi 18 女
    
    3.简略声明
        name, age, hobby := "kimi", 19, "写代码"
    fmt.Println(name, age, hobby)  //kimi 19 写代码
    
    4.完整定义和类型推导混合
      var (
    	name          = "kiki"
    	age    int    = 18
    	gender string = "女"
    )
    fmt.Println(name, age, gender) // kimi 18 女
    
  7. 变量不能重复定义三种定义方式

    var name = "kiki"
    //var name string = "jim"  //不能重复声明变量
    //name := "haha"   //不能重复声明变量
    name, age := "jim", 18   //有新的变量是可以这样重复声明变量
    fmt.Println(name, age)  //jim 18
    
  8. 变量可以先定义在赋值,必须使用完整定义(方式1)

    var name string //默认值是空字符串
    var age int     // 默认值是0
    
    //var age   //不行
    //name  :=   //不行
    //age = 19   // 不行
    
    fmt.Println(name,age)
    
  9. 简略声明用的多

    a :=10
    
  10. 查看变量类型 ```go

       //a := 99
       a := "kimi"
       fmt.Printf("a的值是:%v,类型是:%T", a, a) //a的值是:kimi,类型是:string
    

Go语言变量类型

数字int

数字类型都表示整数,区别就是数字范围不同,一个字节是八个比特位.

32位机器是int32,64位机器是int64

类型 数字范围
int8 -2的7次方,2的7次方-1【-128,127】
int16 -2的15次方,2的15次方-1
int32 -2的31次方,2的31次方-1
int64 -2的63次方,2的63次方-1

正整数uint

类型 数字范围
int8 0,2的7次方-1【-128,127】
int16 0,2的15次方-1
int32 0,2的31次方-1
int64 0,2的63次方-1

浮点数

表示小数,下面是表示小数范围不一样

类型 范围
float32 单精度浮点数,精度为小数点约7位数字
float64 双精度浮点数,精度为小数点约15位数字

浮点数可以进行基本的数学运算,如加减乘除,也可进行比较运算,大于小于等于

复数

类型
complex64 complex64类型表示64位复数,由两个float32类型的实部和虚部组成,占用8个字节
complex128 complex128类型表示128位复数,由两个float64类型的实部和虚部组成,占用16个字节。

rune byte

类型
rune int32的别名,rune可以写字符串,但是取ASCII码
byte uint8的别名,byte 只能写ASCII,不能定义字符串

string

  1. 字符串是用双引号包裹的,不能换号,
  2. 反引号包裹的,可以换行

注意:go也有单引号包裹的,它不是字符串

布尔类型

true

false

常量

关键字const,不可使用简略定义,只能用完整定义和类型推导,常量一旦定义了值就固定了,不允许修改

const name string = "kimi"  //完整定义
const name = "kimi"         // 类型推导

变量、常量的作用域范围

变量只要不再同一个范围内,是可以再定义的

var name = "kiki"

func main() {
	//定义常量
	//const name string = "kimi"
	name = "kimi" //报错
	fmt.Println(name) //kimi
}

iota的使用

  1. 同时定义多个常量,第二个如果没有赋值,初值就是上一个的值
  2. iota用法一,后续定义的变量会自增,后续就可以用等于iota,他会自增
  3. 只要用了iota,后续都会自增1
  4. iota放在第几行,值就是多少,后续都会基于当前行自增

image

函数基础

普通函数--无参

func main() {
	//1.调用普通函数
	test()
    
}

// 1.定义普通函数,没有参数,没有返回值
func test() {
	fmt.Println("我是普通函数")
}

有参函数

有参函数参数有几个就传几个,类型要严格一致且必定要指定参数类型

func main() {
	//2.调用有参数的函数(类型必须严格一致,有几个就传几个值,没有关键字传参
	test1(11, "kimi")
    
    //3.调用简写的有参函数
	test2(11, 12, "kimi")
    
}

// 2.有参数的函数,必须指定参数类型
func test1(a int, b string) {
	fmt.Println(a)
	fmt.Println(b)

}

// 3 有参数的函数,多个参数,类型一致可以简写  (go语言想发设法让你少写代码)
func test2(a, b int, c string) {
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
}

有参函数带返回值

有参函数的返回值的类型要和参数的类型一致,返回值要用变量来接收

func main() {
	//4.调用有返回值的函数
	//test3(1, 2) //需要一个变量来接收
	//res := test3(1, 2)
	//var res = test3(1, 2)
	var res int = test3(1, 2)

	fmt.Println(res)

	// 5 调用多个参数,多个返回值的函数  必须用多个接收,有几个就要用几个变量接收,不能多不能少
	//res1, res2, res3 := test4(4, 5)
	//fmt.Println(res1, res2, res3)  //9 20  成功

	// 5.1 就不想要第三个参数,后面不会用第三个参数
	res1, res2, _ := test4(4, 5)
	fmt.Println(res1, res2) //9 20
	fmt.Println(_)          //不能但变量用,就是个空白
    
}


// 4 既有参数,又有返回值的 ,只有一个返回值的,需要指明返回值类型
func test3(a, b int) int {
	return a + b
}

// 5 多个参数,多个返回值
func test4(a, b int) (int, int, string) {
	return a + b, a * b, "成功"
}


匿名函数

同一个包下,变量和函数只能定义一次.

匿名函数含义

匿名函数:没有函数名,一般定义在其他函数内部

f := func() {

}
func main() {
	//1.匿名函数
	test()

}
// 1 匿名函数加括号直接调用
func test() {
	func() {
		fmt.Println("我是内存函数")
	}() //不直接调用会报错

}

匿名函数赋值给变量

func main() {
	//2.匿名函数赋值给变量
	test1()

}

// 2 匿名函数赋值给一个变量--->函数是一种类型---》在go中,函数又称一等公民(可以赋值给变量的都叫一等公民),又叫头等函数,一等函数
func test1() {
	//f := func() {
	//	fmt.Println("我是内存函数")
	//}

	// f 类型是什么呀?完整定义,写类型
	var f func() = func() {
		fmt.Println("我是内存函数")
	}

	fmt.Printf("%T", f) //func()
}

函数调用后 ---返回闭包函数

函数调用,如果有返回值,才能用变量去接收res := test7()

func main() {
	// 3 调用函数,返回闭包函数
	res := test2()  // 没有test2没有返回值是会报错的
    fmt.Println(res) //func(int)  0x68de00 内存地址
	res()

}

// 3 函数是一等公民,是一种类型---》函数的参数和返回值都是类型的一部分
func test2() int {
	//var f func() = func() {
	//	fmt.Println("我是内层函数")
	//}

	var f1 func(int) int = func(a int) int {
		fmt.Println(a)
		return 10
	}

	var f2 func(int) string = func(c int) string {
		fmt.Println("\n")
		fmt.Println(c)
		return "9"

	}
	//fmt.Printf("%T", f)  //func()
	fmt.Printf("%T", f1) //func()
	f2(1)

	return 8

}

类型重命名、可变长参数、defer

  1. 函数赋值给变量

    函数的参数和返回值都是类型的一部分,函数可以赋值给一个变量

    package main
    import "fmt"
    
    // 1 函数的参数和返回值都是类型的一部分,函数可以赋值给一个变量
    // test3 函数,接收一个参,参数是函数类型:没有参数没有返回值
    // func 有返回值,返回值是个函数:函数有两个参数,一个返回值
    func test3(a func()) func(int, int) int {
        // 函数名--函数参数---返回值类型
    	a()  //
    	return func(x, y int) int {
    		return x + y
    	}
    }
    
    func main() {
    	test3(func() {
    		fmt.Println("被传入的函数")
    	})
    }
    
  2. 类型重命名

    可以给类型重复名(函数\类型)

    // 如果 type Myint int  相当于我们新定义了一个类型,叫Myint
    // 如果 type Myint = int  只是重命名,没有新定义类型
    
    type MyFunc func(int, int) int
    type Myint = int
    
    func test3(a func()) func(int, int) int {
    	a()
    	return func(x, y int) int {
    		return x + y
    }}
    
    func main() {
    	var a Myint = 9
    	var b int = 10
    	//var b int8 = 10  //类型不同不能相加,go是静态强类型
    	fmt.Println(a + b) //19
    
    	var f MyFunc = test3(func() {
    		fmt.Println("被传入的函数")
    	})
    	res := f(8, 9)
    	fmt.Println(res)  //17
    }
    
  3. 函数可变长参数

    // 可以传任意长度的int类型参数
    func test4(a ...int) {
    	fmt.Println(a)          //[1 23 56 778 5578]
    	fmt.Printf("a的类型是:", a)   // 类型是 int类型切片 []int
    	//a的类型是:%!(EXTRA []int=[1 23 56 778 5578])
    
    }
    
    func main() {
    	//可变长参数
    	test4(1, 23, 56, 778, 5578)
    }
    
  4. defer关键字

    遇见defer关键字,延迟调用, 当前函数所有代码都执行完了,再执行defer的内容,先注册,后调用 ,先写的defer后来执行.

    defer fmt.Printf("今天周三了\n")
    defer fmt.Printf("明天周四\n")
    
    fmt.Printf("今天周三了\n")
    fmt.Printf("明天周四\n")
    
    
    """  结果
    今天周三了
    明天周四
    明天周四
    今天周三了
    """
    

包的使用

python模块和包

模块就是一个py文件,是一个文件夹,包下有__init__.py文件

go包

  1. 包是在文件夹下,这个文件夹下所有go文件的第一行要声明一堆go文件的组合

  2. 在go中,一个包就是一个作用域. 新建文件夹,写多个go文件,包名必须要一致.

  3. go中有个 go path 模式,已经弃用了,它的包导入了,不是从项目下开始导入,而是从 go path 的src路径下开始导入

注意点

  1. 包内部,大写开头,表示导出 变量 函数 .....

  2. 包内部的变量函数,只能定义一次

  3. 包内部的所有东西,在包内部直接使用

  4. 包下可以根文件夹名不一样,但是一个文件夹下只能有一个包main包

  5. 导入包,按路径导入,如果不重复名,就是文件夹必须根包名一样

    如果文件夹跟包名不一样,可以命名成任意的,但要重命名导入的包名 ,后续使用重命名的包名.

    import kiki "GoProjectday03/tools"

    可以命名成任意的 import rose "GoProjectday03/tools"

    以后使用包名,调用即可 ---tools.Test()

  6. 包内的init函数,可以定义多次,只要导入包,就会依次执行init函数

    func init() {
    fmt.Printf("我是第一个init函数") }

    func init() {
    fmt.Printf("我是第二个init函数") }

  7. 导入包,必须使用,不使用就报错

    如果只想执行init函数

    import _ "GoProjectday03/tools"

  8. 一个文件夹下可以再建文件夹建新的包,各个文件夹直接没有必然联系,只是文件夹层级关系

  9. 使用的go mod 模式,从1.11后都是这种模式,项目根路径下会有一个go.mod

//tools.go文件
package tools

import "fmt"

func add(a, b int) int {  //小写的函数名是包内使用的函数,可以直接导入
	return a + b

}
//导出的函数名要大写    
func Test() {
	fmt.Printf("我是tools中的函数")
	res := add(1, 2)
	fmt.Println(res)
}


//GoProjectday03下的s2.go文件
package main

import "GoProjectday03/tools"

func main() {
	tools.Test()
}

goSDK内置包---Gin

自定义包和第三方包

gin: 开了一个web服务

gin的使用

  1. 安装

    //配置代理:代理是七牛云
    1.局部配置: 直接在配置中的goland中配置
    	GOPROXY=https://goproxy.cn,direct
    2.全局配置:
    终端命令:go env ---修改set GOPROXY
    
    //下载github的包
     go get  github.com/gin-gonic/gin
    
  2. 使用gin

    package main
    import kiki "GoProjectday03/tools"
    import "github.com/gin-gonic/gin"
    
    func main() {
    	kiki.Test()
    
    	r := gin.Default()
    	//r.LoadHTMLFiles("templates/index.html")   //LoadHTMLFiles指定单个页面
    	r.LoadHTMLGlob("templates/*") //LoadHTMLGlob指定templates下所有的页面
    	r.GET("/", func(c *gin.Context) {
    		c.JSON(200, gin.H{
    			"code":    "100",
    			"message": "成功22",
    		})
    	})   //GET请求路由
    	r.GET("/index", func(c *gin.Context) {
    		c.HTML(200, "index.html", gin.H{"name": "kimi", "age": 18})
    	})
    	r.Run() //listen and serve on 127.0.0.1:8080  //运行
    
    }
    
    // html页面的展示
    <h1>{{.name}}---{{.age}}</h1>
    

if-else

if-else基本格式

if 条件{
    
}else if 条件 {
    
}else {
    
}

代码展示

package main

import "fmt"

func main() {
	score := 96
	if score >= 90 {
		fmt.Printf("优秀")
	} else if score >= 80 && score < 90 {
		fmt.Printf("良好")
	} else if score >= 70 && score < 80 {
		fmt.Println("中等")
	} else if score >= 60 && score < 70 {
		fmt.Println("及格")
	} else {
		fmt.Println("不及格")
	}

}

循环

python   while for
go      只有for循环
java     while\for\do while

//go中的for循环能实现while循环的功能
  1. for基本语法, for i 中的 i的作用域范围只在for内部有效

    func main() {
    	// 1 基本语法  for关键字 定义变量i=0;i<10;i++{}  三部分都可以省略,但是一般会保留第二部分,第二部分是条件
    	// 2 循环打印0--9
    	for i := 0; i < 10; i++ {
    		fmt.Println(i)
    	}
    	//fmt.Println(i)  i的作用域范围只在for内部有效
    }
    
    
  2. 省略第一部分,分号不能少

      	i := 0
      	for ; i < 10; i++ {
      		fmt.Println(i)
      	}
      	fmt.Println(i)  //10
    
  3. 省略第三部分,分号不能少

      	for i := 0; i < 10; {
      		fmt.Println(i)
      		i++
      	}
      
    }
    
  4. 省略第一部分和第三部分,分号能省略

    	i := 0
    	for i < 10 {
    		fmt.Println(i)
    		i++
    	}
    
  5. for条件 {} while 循环

    	for true {
    		fmt.Println("1111")
    	}
    
  6. 死循环

    	i := 0
    	for {
    		fmt.Println(i)
    		i++
    	}
    
  7. 基于迭代的循环

    上面都是基于索引的循环

    	s := "kimi"
    	for i, v := range s {
    		fmt.Println(i)         //0   1
    		fmt.Println(v)         //107  105
    		fmt.Println(string(v)) //k  i
    	}
    
    	l := "中国"
    
    	for i := 0; i < len(l); i++ {
    		//fmt.Println(string(l[i])) // k i m i
    		fmt.Println(l[i]) //228 184 173 229 155 189  ACKII中国两个字所占字符对应的位置
    		//fmt.Println(string(l[i])) //乱码
    	}
    

switch

switch是一个条件语句,用于将表达式的值与可能匹配的选项列表进行比较,并根据匹配情况执行响应的代码块,优雅的替换调else-if

  1. switch基本使用

    	// 1 switch 基本使用
    	score := 78  //条件没有就没有结果,有结果就输对应的结果
    	switch score {
    	case 90:
    		fmt.Println("我是90")
    	case 80:
    		fmt.Println("我是80")
    	case 70:
    		fmt.Println("我是70")
    	}
    
  2. default的使用

    	// 2.default的使用
    	score := 99 //条件没有就没有结果,有结果就输对应的结果
    	switch score {
    	case 90:
    		fmt.Println("我是90")
    	case 80:
    		fmt.Println("我是80")
    	case 70:
    		fmt.Println("我是70")
    	default:
    		fmt.Println("其他分数")
    	}
    
  3. 多表达式判断

    	score := 80
    	switch score {
    	case 90, 91, 92, 93, 94:
    		fmt.Println("我是90,91,92,93,94之一")
    	case 80, 89:
    		fmt.Println("我是80/89")
    	case 70:
    		fmt.Println("我是70")
    	default:
    		fmt.Println("其他分数")
    	}
    
  4. 无表达式

    	score := 81
    	switch {
    	case score > 90:
    		fmt.Println("大于90")
    	case score > 80 && score < 90:
    		fmt.Println("我是80-90")
    	case score >= 60:
    		fmt.Println("大于60")
    	default:
    		fmt.Println("其他分数")
    	}
    
  5. Fallthrougth

    //默认情况下,每个条件之间完,默认加break,但是也不用加,其他语言要加,其他语言去掉break,会无条件执行下一个case
    // 要无条件执行下一个case,需要使用fallthrough
    	score := 86
    	switch {
    	case score > 90:
    		fmt.Println("大于90")
    	case score > 80 && score < 90:
    		fmt.Println("我是80-90")
    		fallthrough
    	case score >= 60:
    		fmt.Println("大于60")
    		fallthrough //无条件执行下一个case
    	default:
    		fmt.Println("其他分数")
    
    	}
    }
    

数组

数组式同一类型元素的集合. 例如 整数集合 5,8,9,79,76 形成一个数组。

go语言中不允许混合不同类型的元素,例如包含字符串和整数的数组,数组式连续存储的,存储同一个类型的数据结构.
数组定义长度就固定了,数组不能扩容,切片才能扩容。

值类型有默认类型,字符串-->"" ,列表--->[],nill

引用类型没有默认值

1. 数组初始化,数组是值类型

数组值类型,go语言中函数传参是 copy传递,复制一份参数,传入 当参数传递,在函数中修改,不会影响原来的

func main() {
	var a [3]int = [3]int{2, 3, 4}  //初始化
	test(a)
	fmt.Println(a)  //[2 3 4]
}

func test(a [3]int) {
	a[0] = 89
	fmt.Println(a, "被调用") //[89 3 4] 被调用

}

2.数组长度

	// 2 数组长度
	//var a = [3]int{1, 2, 3}
	//a := [3]int{1, 2, 3}
	a := [...]int{1, 34, 5, 67, 87, 78} //可变长

	fmt.Println(a)        //[1 34 5 67 87 78]
	fmt.Printf("%T\n", a) //[6]int 数组
	fmt.Println(len(a))   // 6

3. 循环打印数组

	a := [...]int{1, 34, 5, 67, 87, 78}
	//基于索引的
	//for i := 0; i < len(a); i++ {
	//	fmt.Println(a[i])
	//}

	//range循环,基于迭代的
	for i, v := range a { // i是索引,v是值
		fmt.Println(i)
		fmt.Println(v)
	}

4.多维数组

	//var a [3][2]int //只是定义,没有初始化, [[0 0] [0 0] [0 0]]
	var a [3][2]int = [3][2]int{{2, 3}, {2, 5}} //[[2 3] [2 5] [0 0]]
	fmt.Println(a)
	fmt.Println(a[0])
	fmt.Println(a[0][1])

	//循环多维数组
	for i := 0; i < len(a); i++ {
		for j := 0; j < len(a[i]); j++ {
			fmt.Println(a[i][j])
		}
	}

	for _, v := range a { //一般我们不拿索引值,只拿索引对应的值
		for _, v1 := range v {
			fmt.Println(v1)
		}
	}

5. 数组定义并赋值初值

	var a [100]int8 = [100]int8{86: 1, 66: 67}
	fmt.Println(a)

切片

slice:切片是由数组建立的一种方便 灵活且功能强大的包装.切片本身不拥有任何数据.他们只能对现有数组的[引用]

1. 切片的定义

	//1 切片的定义   中括号中不放任何东西,是切片类型,放个数字,就是数组类型
	var a []int         //只定义,没有初始化,是引用类型  是nil类型,等同于python中的None
	fmt.Printf("%T", a) // []int
	fmt.Println(a[1])   //  index out of range [1] with length 0
	if a == nil {
		fmt.Println("我是空数组") //[]int我是空数组
	}

2. 切片定义并初始化

	// 定义并初始化
	var b [10]int      //值类型,有默认值[0 0 0 0 0 0 0 0 0 0]
	var a []int = b[:] /// 表示a这个切片,对数组b进行引用,引用了从0到最后
	fmt.Println(b)     //[0 0 0 0 0 0 0 0 0 0]
	fmt.Println(a)     //[0 0 0 0 0 0 0 0 0 0]
	a[8] = 99
	fmt.Println(a[8]) //99
	fmt.Println(a)    //[0 0 0 0 0 0 0 0 99 0
	fmt.Println(b)    //[0 0 0 0 0 0 0 0 99 0

3.切片的取值赋值

	var b [10]int      //值类型,有默认值[0 0 0 0 0 0 0 0 0 0]
	var a []int = b[:] /// 表示a这个切片,对数组b进行引用,引用了从0到最后
	a[8] = 99
	fmt.Println(a[8]) //99

4.切片的修改,会影响底层数组,数据的修改也会影响切片

	b[0] = 11
	fmt.Println(b) //[11 0 0 0 0 0 0 0 99 0
	fmt.Println(a) //[11 0 0 0 0 0 0 0 99 0

5.基于数组,获得切片

	var b [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	var a []int = b[0:3] // 引用,引用了从0到最后
	fmt.Println(a)       //[1 2 3]
	a[0] = 99
	fmt.Println(a) //[99 2 3]
	fmt.Println(b) //[99 2 3 4 5 6 7 8 9 10]
	var c []int = b[6:9]
	fmt.Println(c) //[7 8 9]
	c[0] = 1000
	fmt.Println(c) //[99 8 9]
	fmt.Println(b) //[99 2 3 4 5 6 1000 8 9 10]
	b[0] = 88
	fmt.Println(b)  //[88 2 3 4 5 6 1000 8 9 10]


//切片追加值
	fmt.Println(a) //[1 2 3]
	a = append(a, 67)
	fmt.Println(a) //[1 2 3 67]
	fmt.Println(b) //[1 2 3 67 5 6 7 8 9 10]

6.切片的长度和容量

	var b [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
	var a []int = b[6:9]
	fmt.Println(a)      //[7 8 9]
	fmt.Println(len(a)) //3
	fmt.Println(cap(a)) //4  //容量,能存多少 4 ,不是底层数组的大小,取决于切片切数组的位置

7.切片如果追加值超过了底层数组长度,会自动扩容

	var b [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
	var a []int = b[6:9]
	a[0] = 100
	fmt.Println(b) //[1 2 3 4 5 6 100 8 9 0]
	fmt.Println(a) //[100 8 9]

	a = append(a, 66)
	fmt.Println(a)      //[100 8 9 66]
	fmt.Println(b)      //[1 2 3 4 5 6 100 8 9 66]
	fmt.Println(len(a)) //4
	fmt.Println(cap(a)) //4

	// 超过底层数组,追加值
	a = append(a, 88)
	fmt.Println(a)      // [100 8 9 66 88]
	fmt.Println(len(a)) // 5
	fmt.Println(cap(a)) // 8  超过容量,翻倍扩容(容量在1024内)
	a = append(a, 12, 34, 45, 67)
	fmt.Println(a)      // [100 8 9 66 88 12 34 45 67]
	fmt.Println(len(a)) // 9
	fmt.Println(cap(a)) // 16  超过容量,翻倍扩容(容量在1024内)

	// 改底层数组
	b[9] = 999
	fmt.Println(b) //[1 2 3 4 5 6 100 8 9 999]
	fmt.Println(a) //[100 8 9 66 88 12 34 45 67]

image

8.切片定义并初始化,使用数组初始化

	//  切片定义并初始化  使用数组初始化
	//使用make初始化
	//var a []int // 是nil ,没有初始化
	//var a []int = make([]int, 3, 4) // 要初始化 ,长度为3,容量为4
	//fmt.Println(a)
	//fmt.Println(len(a)) //3
	//fmt.Println(cap(a)) //4

	var a []int = make([]int, 0, 4) // 要初始化 ,长度为0,容量为4
	fmt.Println(a)                  //[]  有可能是nil,有可能已经初始化完成了,只有初始化完成了才能用
	fmt.Println(len(a))             //0
	fmt.Println(cap(a))             //4

	a = append(a, 5)
	fmt.Println(a)  //[5]
	fmt.Println(len(a)) //1
	fmt.Println(cap(a)) //4

9.切片的参数传递是引用类型,函数中修改值会影响原来的

函数中修改和新增会改变原来的数组值,但是a的长度只有3,所以取出来就是[88 99 0]

	var a []int = make([]int, 3, 5)
	a[1] = 99 ////[0 99 0]
	test(a)
	fmt.Println(a) //[88 99 0]

}

func test(a []int) {
	a[0] = 88
	fmt.Println(a) //[88 99 0]
	a = append(a, 33)
	fmt.Println(a)      //[88 99 0 33]  //超过长度会翻倍扩容
	fmt.Println(cap(a)) //5

}

10.多维切片,切片定义并初始化

	//var a []int = []int{2, 3, 4, 45}
	//fmt.Println(a)      //[2 3 4 45]
	//fmt.Println(len(a)) //4
	//fmt.Println(cap(a)) //4
	//a = append(a, 55)
	//fmt.Println(len(a)) //5
	//fmt.Println(cap(a)) //8  扩容翻倍

	var a [][]string = [][]string{{"1", "3"}, {"0"}, {"4", "6", "34"}}
	fmt.Println(a) //[[1 3] [0] [4 6 34]]

	var b [][]string = make([][]string, 3, 3)
	fmt.Println(b[2]) //[]

可变长参数

func main() {
	test("1", "kimi")
	var a []string = []string{"skh", "nihao"}
	test(a...) //相对于打散了传入

}

func test(a ...string) {
	fmt.Println(a)      //[1 kimi]
	fmt.Printf("%T", a) //[]string
	fmt.Println(cap(a)) //2  可变长的容器就是放入的数组个数

}

maps

对于go来说 : key必须是值类型

对于panpython的key必须是不可变类型

以后所有的引用类型,都需要初始化才能用,值类型不需要,有默认值

func main() {

	//1 key -value 形式存储 定义一个map
	//var userInfo map[int]string // 没有初始化,它是nil,但是打印出来不是nil
	//fmt.Println(userInfo)       //map[]
	//if userInfo == nil {
	//	fmt.Println("asdfasdf")
	//}

	// 2 map 初始化 方式一
	//var userInfo map[int]string = map[int]string{1: "kimi", 3: "rose"}
	//fmt.Println(userInfo)  //map[1:kimi 3:rose]
	//if userInfo == nil {
	//	fmt.Println("asdfasdf")
	//}

	// 2 map 初始化 方式二  make初始化
	//var userInfo map[int]string = make(map[int]string)
	//fmt.Println(userInfo) //map[]
	//if userInfo == nil {
	//	fmt.Println("asdfasdf")  //不走
	//}

	// 3 初始化后才能取值赋值
	//var userInfo map[int]string
	//var userInfo map[int]string = make(map[int]string)
	//fmt.Println(userInfo[1]) //报错
	//userInfo[1] = "kiki"
	//fmt.Println(userInfo) //map[1:kiki]
	// 以后所有的引用类型,都需要初始化才能用,值类型不需要,有默认值

	//4 取值赋值
	//var userInfo map[string]string = make(map[string]string)
	//userInfo["age"] = "19"
	//userInfo["name"] = "kimi"
	//fmt.Println(userInfo) //map[age:19 name:kimi]
	//
	//fmt.Println(userInfo["age"]) //19
	//// 取不存在的----》显示value值的默认值
	//fmt.Println("--", userInfo["hobby"]) //空字符串
	//
	//// 如何判断一个key在不在map中   按key取值,能返回一个布尔值,根据布尔值判断
	//v, ok := userInfo["name"]
	//fmt.Println(v)  //kimi
	//fmt.Println(ok) //true就存在,false就不存在
	//
	//if v, ok := userInfo["name"]; ok {
	//	fmt.Println(v) //kimi
	//}

	// 5 删除map元素
	//var userInfo map[string]string = make(map[string]string)
	//userInfo["age"] = "19"
	//userInfo["name"] = "kimi"
	//delete(userInfo, "name")
	//fmt.Println(userInfo)  //map[age:19 name:kimi]

	// 6 map的长度
	//var userInfo map[string]string = make(map[string]string)
	//fmt.Println(len(userInfo)) //0
	//userInfo["age"] = "19"
	//userInfo["name"] = "kimi"
	//fmt.Println(len(userInfo)) //2

	// 7 引用类型
	var userInfo map[string]string = make(map[string]string)
	fmt.Println(len(userInfo))
	userInfo["age"] = "19"
	userInfo["name"] = "kimi"
	test6(userInfo)  //map[age:19 name:rose]
	fmt.Println(userInfo)  //map[age:19 name:rose]

}
func test6(u map[string]string) {
	u["name"] = "rose"
	fmt.Println(u) //0

}

posted @ 2023-04-24 16:59  魔女宅急便  阅读(38)  评论(0)    收藏  举报
Title