Go编程入门详细例子

变量

	//golang的变量使用方式1
	//第一种:指定变量类型,声明后若不赋值,使用默认值
	// int 的默认值是0 , 其它数据类型的默认值后面马上介绍
	var i int 
	fmt.Println("i=", i)

	//第二种:根据值自行判定变量类型(类型推导)
	var num  = 10.11
	fmt.Println("num=", num)

	//第三种:省略var, 注意 :=左侧的变量不应该是已经声明过的,否则会导致编译错误
	//下面的方式等价 var name string   name = "tom"
	// := 的 :不能省略,否则错误
	name := "tom"
	fmt.Println("name=", name)

类型转换

	var i int32 = 100
	//希望将 i => float
	var n1 float32 = float32(i)
	var n2 int8 = int8(i)
	var n3 int64 = int64(i) //低精度->高精度

	fmt.Printf("i=%v n1=%v n2=%v n3=%v \n", i ,n1, n2, n3)
	
	//被转换的是变量存储的数据(即值),变量本身的数据类型并没有变化
	fmt.Printf("i type is %T\n", i) // int32

	//在转换中,比如将 int64  转成 int8 【-128---127】 ,编译时不会报错,
	//只是转换的结果是按溢出处理,和我们希望的结果不一样
	var num1 int64 = 999999
	var num2 int8 = int8(num1) // 
	fmt.Println("num2=", num2)

字符串

//string的基本使用
	var address string = "hello go!"
	fmt.Println(address)

	//字符串一旦赋值了,字符串就不能修改了:在Go中字符串是不可变的
	//var str = "hello"
	//str[0] = 'a' //这里就不能去修改str的内容,即go中的字符串是不可变的。

	//字符串的两种表示形式(1) 双引号, 会识别转义字符(2) 反引号,
	//以字符串的原生形式输出,包括换行和特殊字符,可以实现防止攻击、
	//输出源代码等效果  【案例演示】
	str2 := "abc\nabc"
	fmt.Println(str2)

	//使用的反引号 ``
	str3 := ` 
	package main
	import (
		"fmt"
		"unsafe"
	)
	
	//演示golang中bool类型使用
	func main() {
		var b = false
		fmt.Println("b=", b)
		//注意事项
		//1. bool类型占用存储空间是1个字节
		fmt.Println("b 的占用空间 =", unsafe.Sizeof(b) )
		//2. bool类型只能取true或者false
		
	}
	`
	fmt.Println(str3)

	//字符串拼接方式
	var str = "hello " + "world"
	str += " haha!"

	fmt.Println(str)
	//当一个拼接的操作很长时,怎么办,可以分行写,但是注意,需要将+保留在上一行.
	str4 := "hello " + "world" + "hello " + "world" + "hello " + 
	"world" + "hello " + "world" + "hello " + "world" + 
	"hello " + "world"
	fmt.Println(str4)


	var a int // 0
	var b float32 // 0
	var c float64 // 0
	var isMarried bool // false 
	var name string // ""
	//这里的%v 表示按照变量的值输出
	fmt.Printf("a=%d,b=%v,c=%v,isMarried=%v name=%v",a,b,c,isMarried, name)

string转成基本数据类型

var str string = "true"
	var b bool
	// b, _ = strconv.ParseBool(str)
	// 说明
	// 1. strconv.ParseBool(str) 函数会返回两个值 (value bool, err error)
	// 2. 因为我只想获取到 value bool ,不想获取 err 所以我使用_忽略
	b , _ = strconv.ParseBool(str)
	fmt.Printf("b type %T  b=%v\n", b, b)
	
	var str2 string = "1234590"
	var n1 int64
	var n2 int
	n1, _ = strconv.ParseInt(str2, 10, 64)
	n2 = int(n1)
	fmt.Printf("n1 type %T  n1=%v\n", n1, n1)
	fmt.Printf("n2 type %T n2=%v\n", n2, n2)

	var str3 string = "123.456"
	var f1 float64
	f1, _ = strconv.ParseFloat(str3, 64)
	fmt.Printf("f1 type %T f1=%v\n", f1, f1)


	//注意:
	var str4 string = "hello"
	var n3 int64 = 11
	n3, _ = strconv.ParseInt(str4, 10, 64)
	fmt.Printf("n3 type %T n3=%v\n", n3, n3)

基本数据类型转string

	var num1 int = 99
	var num2 float64 = 23.456
	var b bool = true
	var myChar byte = 'h'
	var str string //空的str

	//使用第一种方式来转换 fmt.Sprintf方法

	str = fmt.Sprintf("%d", num1)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%f", num2)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%t", b)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%c", myChar)
	fmt.Printf("str type %T str=%q\n", str, str)

	//第二种方式 strconv 函数 
	var num3 int = 99
	var num4 float64 = 23.456
	var b2 bool = true

	str = strconv.FormatInt(int64(num3), 10)
	fmt.Printf("str type %T str=%q\n", str, str)
	
	// strconv.FormatFloat(num4, 'f', 10, 64)
	// 说明: 'f' 格式 10:表示小数位保留10位 64 :表示这个小数是float64
	str = strconv.FormatFloat(num4, 'f', 10, 64)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = strconv.FormatBool(b2)
	fmt.Printf("str type %T str=%q\n", str, str)

	//strconv包中有一个函数Itoa
	var num5 int64 = 4567
	str = strconv.Itoa(int(num5))
	fmt.Printf("str type %T str=%q\n", str, str)

指针

	//基本数据类型在内存布局
	var i int = 10
	// i 的地址是什么,&i
	fmt.Println("i的地址=", &i)
	
	//下面的 var ptr *int = &i
	//1. ptr 是一个指针变量
	//2. ptr 的类型 *int
	//3. ptr 本身的值&i
	var ptr *int = &i 
	fmt.Printf("ptr=%v\n", ptr)
	fmt.Printf("ptr 的地址=%v", &ptr) 
	fmt.Printf("ptr 指向的值=%v", *ptr)

算术运算符

	//重点讲解 /、%
	//说明,如果运算的数都是整数,那么除后,去掉小数部分,保留整数部分
	fmt.Println(10 / 4) 
	
	var n1 float32 = 10 / 4 //
	fmt.Println(n1)

	//如果我们希望保留小数部分,则需要有浮点数参与运算
	var n2 float32 = 10.0 / 4
	fmt.Println(n2)

	// 演示  % 的使用 
	// 看一个公式 a % b = a - a / b * b
	// fmt.Println("10%3=", 10 % 3) // =1
	// fmt.Println("-10%3=", -10 % 3) // = -10 - (-10) / 3 * 3 = -10 - (-9) = -1
	// fmt.Println("10%-3=", 10 % -3) // =1
	// fmt.Println("-10%-3=", -10 % -3) // =-1

	// ++ 和 --的使用
	var i int = 10
	i++ // 等价 i = i + 1
	fmt.Println("i=", i) // 11
	i-- // 等价 i = i - 1
	fmt.Println("i=", i) // 10

	if i > 0 {
		fmt.Println("ok")
	}

关系运算符

	//演示关系运算符的使用
	var n1 int = 9
	var n2 int = 8
	fmt.Println(n1 == n2) //false
	fmt.Println(n1 != n2) //true
	fmt.Println(n1 > n2) //true
	fmt.Println(n1 >= n2) //true
	fmt.Println(n1 < n2) //flase
	fmt.Println(n1 <= n2) //flase
	flag := n1 > n2
	fmt.Println("flag=", flag)

逻辑运算符

	//演示逻辑运算符的使用  &&
	var age int = 40
	if age > 30 && age < 50 {
		fmt.Println("ok1")
	}

	if age > 30 && age < 40 {
		fmt.Println("ok2")
	}

	//演示逻辑运算符的使用  ||

	if age > 30 || age < 50 {
		fmt.Println("ok3")
	}

	if age > 30 || age < 40 {
		fmt.Println("ok4")
	}

	//演示逻辑运算符的使用  !

	if age > 30 {
		fmt.Println("ok5")
	}

	if !(age > 30) {
		fmt.Println("ok6")
	}

进制

对于整数,有四种表示方式:
二进制:0,1 ,满 2 进 1。
在 golang 中,不能直接使用二进制来表示一个整数,它沿用了 c 的特点。
十进制:0-9 ,满 10 进 1。
八进制:0-7 ,满 8 进 1. 以数字 0 开头表示。
十六进制:0-9 及 A-F,满 16 进 1. 以 0x 或 0X 开头表示。此处的 A-F 不区分大小写。

二进制转十进制
Go编程入门详细例子_第1张图片
八进制转换成十进制
Go编程入门详细例子_第2张图片
16 进制转成 10 进制

十进制如何转二进制
Go编程入门详细例子_第3张图片
十进制转成八进制
Go编程入门详细例子_第4张图片
十进制转十六进制
Go编程入门详细例子_第5张图片
二进制转换成八进制

二进制转成十六进制

八进制转换成二进制

十六进制转成二进制

进制输出

	var i int = 5
	//二进制输出 
	fmt.Printf("%b \n", i)

	//八进制:0-7 ,满8进1. 以数字0开头表示
	var j int = 011 // 011=> 9
	fmt.Println("j=", j)

	//0-9及A-F,满16进1. 以0x或0X开头表示
	var k int = 0x11 // 0x11=> 16 + 1 = 17
	fmt.Println("k=", k)

原码、反码、补码

网上对原码,反码,补码的解释过于复杂,我这里精简6句话:
1)对于有符号的而言:

二进制的最高位是符号位:0表示正数,1表示负数

1==>[00001] -1==>[1000 0001] 

2)正数的原码,反码,补码都一样

3)负数的反码=它的原码符号位不变,其它位取反(0->1,1->0)

 1=>原码[0000 0001]反码[000 001]补码[0000 0001]

 -1=>原码[1000 0001]反码[1111110] 补码[1111 1111]
 

4)负数的补码=它的反码+1
5)0的反码,补码都是0

6)在计算机运算的时候,都是以补码的方式来运算的.

1+1 1-1=1+(-1)

位运算符和移位运算符

按位与& : 两位全为1,结果为 1,否则为 0
按位或| : 两位有一个为 1,结果为 1,否则为 0
按位异或 ^ : 两位一个为 0,一个为 1,结果为 1,否则为 0
右移运算符 >>:低位溢出,符号位不变,并用符号位补溢出的高位
左移运算符 <<: 符号位不变,低位补 0

a := 1 >> 2	// 0000 0001 =>0000 0000 = 0
c := 1 << 2	// 0000 0001 ==> 0000 0100 => 4
	//位运算的演示
	fmt.Println(2&3) // 2
	fmt.Println(2|3) // 3
	fmt.Println(2^3) // 3
	fmt.Println(-2^2) //-4

	a := 1 >> 2 //0
	c := 1 << 2 //4
	fmt.Println("a=", a, "c=", c)

分支控制

	//代码
	var age int
	fmt.Println("请输入年龄:")
	fmt.Scanln(&age)

	if age > 18 {
		fmt.Println("你年龄大于18~....")
	} else {
		fmt.Println("你的年龄不大这次放过你了")
	}

	//golang支持在if中,直接定义一个变量,比如下面
	if age1 := 20; age1 > 18 {
		fmt.Println("你年龄大于18,要对自己的行为负责!")
	}
	

switch分支

	//switch 后也可以不带表达式,类似 if --else分支来使用。【案例演示】
	var age int = 10
	
	switch {
		case age == 10 :
			fmt.Println("age == 10")
		case age == 20 :
			fmt.Println("age == 20")
		default :
			fmt.Println("没有匹配到")
	}
	
	//case 中也可以对 范围进行判断
	var score int = 90
	switch {
		case score > 90 :
			fmt.Println("成绩优秀..")
		case score >=70 && score <= 90 :
			fmt.Println("成绩优良...")
		case score >= 60 && score < 70 :
			fmt.Println("成绩及格...")
		default :
			fmt.Println("不及格")
	}


	//switch 后也可以直接声明/定义一个变量,分号结束,不推荐
	
	switch grade := 90; { // 在golang中,可以这样写
		case grade > 90 :
			fmt.Println("成绩优秀~..")
		case grade >=70 && grade <= 90 :
			fmt.Println("成绩优良~...")
		case grade >= 60 && grade < 70 :
			fmt.Println("成绩及格~...")
		default :
			fmt.Println("不及格~")
	}

	//switch 穿透-fallthrough ,如果在 case 语句块后增加 fallthrough ,则会继续执行下一个 case,也叫 switch 穿透
	var num int = 10
	switch num {
		case 10:
			fmt.Println("ok1")
			fallthrough //默认只能穿透一层
		case 20:
			fmt.Println("ok2")
			fallthrough
		case 30:
			fmt.Println("ok3")	
		default:
			fmt.Println("没有匹配到..")
	}

for循环

	//golang中,有循环控制语句来处理循环的执行某段代码的方法->for循环
	//for循环快速入门
	
	for i := 1; i <= 10; i++ {
		fmt.Println("你好,尚硅谷", i)
	}

	//for循环的第二种写法
	j := 1 //循环变量初始化
	for j <= 10 { //循环条件
		
		fmt.Println("你好,尚硅谷~", j)
		j++ //循环变量迭代
	}

	//for循环的第三种写法, 这种写法通常会配合break使用
	k := 1
	for {  // 这里也等价 for ; ; { 
		if k <= 10 {
			fmt.Println("ok~~", k)
		} else {
			break //break就是跳出这个for循环
		}
		k++
	}


	//字符串遍历方式1-传统方式
	// var str string = "hello,world!北京"
	// for i := 0; i < len(str); i++ {
	// 	fmt.Printf("%c \n", str[i]) //使用到下标...
	// }

	//字符串遍历方式1-传统方式
	var str string = "hello,world!北京"
	str2 := []rune(str) // 就是把 str 转成 []rune
	for i := 0; i < len(str2); i++ {
		fmt.Printf("%c \n", str2[i]) //使用到下标...
	}

	fmt.Println()
	//字符串遍历方式2-for-range
	str = "abc~ok上海"
	for index, val := range str {
		fmt.Printf("index=%d, val=%c \n", index, val)
	}

do…while

	//使用while方式输出10句 "hello,world"
	//循环变量初始化
	var i int = 1
	for {
		if i > 10 { //循环条件
			break // 跳出for循环,结束for循环
		}
		fmt.Println("hello,world", i)
		i++ //循环变量的迭代
	}

	fmt.Println("i=", i)


	//使用的do...while实现完成输出10句”hello,ok“
	var j int = 1
	for {
		fmt.Println("hello,ok", j)
		j++ //循环变量的迭代
		if j > 10 {
			break //break 就是跳出for循环
		}
	}

函数1

package main
import (
	"fmt"
)

//一个函数 test
func test(n1 int) {

	n1 = n1 + 1
	fmt.Println("test() n1=", n1) //?输出结果= ?
}

//一个函数 getSum
//
func getSum(n1 int , n2 int) int {
	sum := n1 + n2
	fmt.Println("getSum sum = ", sum)  // 30
	//当函数有return语句时,就是将结果返回给调用者
	//即谁调用我,就返回给谁
	return sum 
}

//请编写要给函数,可以计算两个数的和和差,并返回结果
func getSumAndSub(n1 int, n2 int) (int, int) {
	sum := n1 + n2
	sub := n1 - n2
	return sum, sub
}

func main() {

	n1 := 10
	//调用test
	test(n1)
	fmt.Println("main() n1=", n1)//?输出结果= ?

	
	sum := getSum(10, 20)
	fmt.Println("main sum = ", sum) // 30

	//调用getSumAndSub
	res1, res2 := getSumAndSub(1, 2) //res1 = 3 res2 = -1
	fmt.Printf("res1=%v res2=%v\n", res1, res2)

	//希望忽略某个返回值,则使用 _ 符号表示占位忽略
	_, res3 =  getSumAndSub(3, 9)
	fmt.Println("res3=", res3)
}

函数2

package main
import (
	"fmt"
)

//在Go中,函数也是一种数据类型,
//可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用

func getSum(n1 int, n2 int) int {
	return n1 + n2
}

//函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用
func myFun(funvar func(int, int) int, num1 int, num2 int ) int {
	return funvar(num1, num2)
}

//再加一个案例
//这时 myFun 就是 func(int, int) int类型
type myFunType func(int, int) int

//函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用
func myFun2(funvar myFunType, num1 int, num2 int ) int {
	return funvar(num1, num2)
}

//支持对函数返回值命名
func getSumAndSub(n1 int, n2 int) (sum int, sub int){
	sub = n1 - n2
	sum = n1 + n2
	return
}

//案例演示: 编写一个函数sum ,可以求出  1到多个int的和
//可以参数的使用
func sum(n1 int, args... int) int {
	sum := n1 
	//遍历args 
	for i := 0; i < len(args); i++ {
		sum += args[i]  //args[0] 表示取出args切片的第一个元素值,其它依次类推
	}
	return sum
}

func main() {
	
	a := getSum
	fmt.Printf("a的类型%T, getSum类型是%T\n", a, getSum)

	res := a(10, 40) // 等价  res := getSum(10, 40)
	fmt.Println("res=", res)

	//看案例
	res2 := myFun(getSum, 50, 60)
	fmt.Println("res2=", res2)

	// 给int取了别名 , 在go中 myInt 和 int 虽然都是int类型,但是go认为myInt和int两个类型
	type myInt int 

	var num1 myInt // 
	var num2 int
	num1 = 40
	num2 = int(num1) //各位,注意这里依然需要显示转换,go认为myInt和int两个类型
	fmt.Println("num1=", num1, "num2=",num2)


	//看案例
	res3 := myFun2(getSum, 500, 600)
	fmt.Println("res3=", res3)


	//看案例
	a1, b1 := getSumAndSub(1, 2)
	fmt.Printf("a=%v b=%v\n", a1, b1)

	//测试一下可变参数的使用
	res4 := sum(10, 0, -1, 90, 10,100)
	fmt.Println("res4=", res4)

}

值类型和引用类型

1)值类型:基本数据类型 int 系列, float 系列, bool, string 、数组和结构体 struct
2)引用类型:指针、slice 切片、map、管道 chan、interface 等都是引用类型

值传递和引用传递

package main
import (
	"fmt"
)


//函数中的变量是局部的,函数外不生效
func test() {
	//n1 是 test函数的局部变量, 只能在test函数中使用
	//var n1 int = 10
}

func test02(n1 int) {
	n1 = n1 + 10
	fmt.Println("test02() n1= ", n1)
}

// n1 就是 *int 类型
func test03(n1 *int) {
	fmt.Printf("n1的地址 %v\n",&n1)
	*n1 = *n1 + 10
	fmt.Println("test03() n1= ", *n1) // 30
}


func main() {
	// num := 20
	// test02(num)
	// fmt.Println("main() num= ", num)

	num := 20
	fmt.Printf("num的地址=%v\n", &num)
	test03(&num)
	fmt.Println("main() num= ", num) // 30
}

init

package main
import (
	"fmt"
	//引入包
	"helloGo/chapter06/funcinit/utils"
)

var age = test()

//为了看到全局变量是先被初始化的,我们这里先写函数
func test() int {
	fmt.Println("test()") //1
	return 90
}

//init函数,通常可以在init函数中完成初始化工作
func init() {
	fmt.Println("init()...") //2
}

func main() {
	fmt.Println("main()...age=", age) //3
	fmt.Println("Age=", utils.Age, "Name=", utils.Name)

}

包 utils/utils.go

package utils
import "fmt"
var Age int
var Name string

//Age 和 Name 全局变量,我们需要在main.go 使用
//但是我们需要初始化Age 和 Name

//init 函数完成初始化工作
func init() {
	fmt.Println("utils 包的  init()...")
	Age = 100
	Name = "tom~"
}

defer

package main
import (
	"fmt"
)

func sum(n1 int, n2 int) int {
	
	//当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈(defer栈)
	//当函数执行完毕后,再从defer栈,按照先入后出的方式出栈,执行
	defer fmt.Println("ok1 n1=", n1) //defer 3. ok1 n1 = 10
	defer fmt.Println("ok2 n2=", n2) //defer 2. ok2 n2= 20
	//增加一句话
	n1++ // n1 = 11
	n2++ // n2 = 21
	res := n1 + n2 // res = 32
	fmt.Println("ok3 res=", res) // 1. ok3 res= 32
	return res

}

func main() {
	res := sum(10, 20)
	fmt.Println("res=", res)  // 4. res= 32
}
	

error

package main
import (
	"fmt"
	_ "time"
	"errors"
)

func test() {
	//使用defer + recover 来捕获和处理异常
	defer func() {
		err := recover()  // recover()内置函数,可以捕获到异常
		if err != nil {  // 说明捕获到错误
			fmt.Println("err=", err)
			//这里就可以将错误信息发送给管理员....
			fmt.Println("发送邮件给[email protected]~")
		}
	}()
	num1 := 10
	num2 := 0
	res := num1 / num2
	fmt.Println("res=", res)
}

//函数去读取以配置文件init.conf的信息
//如果文件名传入不正确,我们就返回一个自定义的错误
func readConf(name string) (err error) {
	if name == "config.ini" {
		//读取...
		return nil
	} else {
		//返回一个自定义错误
		return errors.New("读取文件错误..")
	}
}

func test02() {

	err := readConf("config2.ini")
	if err != nil {
		//如果读取文件发送错误,就输出这个错误,并终止程序
		panic(err)
	}
	fmt.Println("test02()继续执行....")
}
	

func main() {

	//测试
	// test()
	// for {
	// 	fmt.Println("main()下面的代码...")
	// 	time.Sleep(time.Second)
	// }

	//测试自定义错误的使用

	test02()
	fmt.Println("main()下面的代码...")
}

数组1

package main
import (
	"fmt"
)

func main() {

	var intArr [3]int //int占8个字节
	//当我们定义完数组后,其实数组的各个元素有默认值 0
	fmt.Println(intArr)
	intArr[0] = 10
	intArr[1] = 20
	intArr[2] = 30
	fmt.Println(intArr)
	fmt.Printf("intArr的地址=%p intArr[0] 地址%p intArr[1] 地址%p intArr[2] 地址%p\n", 
		&intArr, &intArr[0], &intArr[1], &intArr[2])


	//从终端循环输入5个成绩,保存到float64数组,并输出.
	// var score [5]float64

	// for i := 0; i < len(score); i++ {
	// 	fmt.Printf("请输入第%d个元素的值\n", i+1)
	// 	fmt.Scanln(&score[i])
	// }
	
	// //变量数组打印
	// for i := 0; i < len(score); i++ {
	// 	fmt.Printf("score[%d]=%v\n", i, score[i])
	// }

	//四种初始化数组的方式
	var numArr01 [3]int = [3]int{1, 2, 3}
	fmt.Println("numArr01=", numArr01)

	var numArr02 = [3]int{5, 6, 7}
	fmt.Println("numArr02=", numArr02)
	//这里的 [...] 是规定的写法
	var numArr03 = [...]int{8, 9, 10}
	fmt.Println("numArr03=", numArr03)

	var numArr04 = [...]int{1: 800, 0: 900, 2:999}
	fmt.Println("numArr04=", numArr04)

	//类型推导
	strArr05 := [...]string{1: "tom", 0: "jack", 2:"mary"}
	fmt.Println("strArr05=", strArr05)
}

数组2

package main
import (
	"fmt"
)

//函数
func test01(arr [3]int) {
	arr[0] = 88
} 

//函数
func test02(arr *[3]int) {
	fmt.Printf("arr指针的地址=%p", &arr)
	(*arr)[0] = 88 //!!
} 

func main() {
	
	/*
	//数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的, 不能动态变化。
	var arr01 [3]int
	arr01[0] = 1
	arr01[1] = 30
	//这里会报错
	arr01[2] = 1.1  
	//其长度是固定的, 不能动态变化,否则报越界
	arr01[3] = 890

	fmt.Println(arr01)
	*/

	//数组创建后,如果没有赋值,有默认值(零值)
	//1. 数值(整数系列, 浮点数系列) =>0
	//2. 字符串 ==> ""
	//3. 布尔类型 ==> false

	var arr01 [3]float32
	var arr02 [3]string
	var arr03 [3]bool
	fmt.Printf("arr01=%v arr02=%v arr03=%v\n", arr01, arr02, arr03)

	//数组的下标是从0开始的

	// var arr04 [3]string // 0 - 2
	// var index int = 3
	// arr04[index] = "tom" // 因为下标是 0 - 2 ,因此arr04[3]就越界

	//Go的数组属值类型, 在默认情况下是值传递, 因此会进行值拷贝。数组间不会相互影响

	// arr := [3]int{11, 22, 33}
	// test01(arr)
	// fmt.Println("main arr=", arr) //


	arr := [3]int{11, 22, 33}
	fmt.Printf("arr 的地址=%p", &arr)
	test02(&arr)
	fmt.Println("main arr=", arr)
}

for-range遍历数组

package main
import (
	"fmt"
)

func main() {

	//演示for-range遍历数组
	 heroes  := [...]string{"宋江", "吴用", "卢俊义"}
	//使用常规的方式遍历,我不写了..

	for i, v := range heroes {
		fmt.Printf("i=%v v=%v\n", i , v)
		fmt.Printf("heroes[%d]=%v\n", i, heroes[i])
	}

	for _, v := range heroes {
		fmt.Printf("元素的值=%v\n", v)
	}
}

切片

package main
import (
	"fmt"
)

func main() {

	//演示切片的基本使用
	var intArr [5]int = [...]int{1, 22, 33, 66, 99}
	//声明/定义一个切片
	//slice := intArr[1:3]
	//1. slice 就是切片名
	//2. intArr[1:3] 表示 slice 引用到intArr这个数组 
	//3. 引用intArr数组的起始下标为 1 , 最后的下标为3(但是不包含3)    
	slice := intArr[1:3] 
	fmt.Println("intArr=", intArr)
	fmt.Println("slice 的元素是 =", slice) //  22, 33
	fmt.Println("slice 的元素个数 =", len(slice)) // 2
	fmt.Println("slice 的容量 =", cap(slice)) // 切片的容量是可以动态变化  

	fmt.Printf("intArr[1]的地址=%p\n", &intArr[1])
	fmt.Printf("slice[0]的地址=%p slice[0==%v\n", &slice[0], slice[0])
	slice[1] = 34
	fmt.Println()
	fmt.Println()
	fmt.Println("intArr=", intArr)
	fmt.Println("slice 的元素是 =", slice) //  22, 33

}

切片2

package main
import (
	"fmt"
)

func main() {

	//演示切片的使用 make
	var slice []float64 = make([]float64, 5, 10)
	slice[1] = 10
	slice[3] = 20
	//对于切片,必须make使用.
	fmt.Println(slice)
	fmt.Println("slice的size=", len(slice))
	fmt.Println("slice的cap=", cap(slice))


	//方式3
	fmt.Println()
	//第3种方式:定义一个切片,直接就指定具体数组,使用原理类似make的方式
	var strSlice []string = []string{"tom", "jack", "mary"}
	fmt.Println("strSlice=", strSlice)
	fmt.Println("strSlice size=", len(strSlice)) //3
	fmt.Println("strSlice cap=", cap(strSlice)) // ?

}

遍历切片

package main
import (
	"fmt"
)

func main() {

	//使用常规的for循环遍历切片
	var arr [5]int = [...]int{10, 20, 30, 40, 50}
	//slice := arr[1:4] // 20, 30, 40
	slice := arr[1:4]
	for i := 0; i < len(slice); i++ {
		fmt.Printf("slice[%v]=%v ", i, slice[i])
	}

	fmt.Println()
	//使用for--range 方式遍历切片
	for i, v := range slice {
		fmt.Printf("i=%v v=%v \n", i, v)
	}

	slice2 := slice[1:2] //  slice [ 20, 30, 40]    [30]
	slice2[0] = 100  // 因为arr , slice 和slice2 指向的数据空间是同一个,因此slice2[0]=100,其它的都变化

	fmt.Println("slice2=", slice2)
	fmt.Println("slice=", slice)
	fmt.Println("arr=", arr)

	fmt.Println()

	//用append内置函数,可以对切片进行动态追加
	var slice3 []int = []int{100, 200, 300}
	//通过append直接给slice3追加具体的元素
	slice3 = append(slice3, 400, 500, 600)
	fmt.Println("slice3", slice3) //100, 200, 300,400, 500, 600

	//通过append将切片slice3追加给slice3
	slice3 = append(slice3, slice3...) // 100, 200, 300,400, 500, 600 100, 200, 300,400, 500, 600
	fmt.Println("slice3", slice3)


	//切片的拷贝操作
	//切片使用copy内置函数完成拷贝,举例说明
	fmt.Println()
	var slice4 []int = []int{1, 2, 3, 4, 5}
	var slice5 = make([]int, 10)
	copy(slice5, slice4)
	fmt.Println("slice4=", slice4)// 1, 2, 3, 4, 5
	fmt.Println("slice5=", slice5) // 1, 2, 3, 4, 5, 0 , 0 ,0,0,0
}

string切片

package main
import (
	"fmt"
)

func main() {

	//string底层是一个byte数组,因此string也可以进行切片处理
	str := "hello@atguigu"
	//使用切片获取到 atguigu
	slice := str[6:] 
	fmt.Println("slice=", slice)

	//string是不可变的,也就说不能通过 str[0] = 'z' 方式来修改字符串 
	//str[0] = 'z' [编译不会通过,报错,原因是string是不可变]

	//如果需要修改字符串,可以先将string -> []byte / 或者 []rune -> 修改 -> 重写转成string
	// "hello@atguigu" =>改成 "zello@atguigu"
	// arr1 := []byte(str) 
	// arr1[0] = 'z'
	// str = string(arr1)
	// fmt.Println("str=", str)

	// 细节,我们转成[]byte后,可以处理英文和数字,但是不能处理中文
	// 原因是 []byte 字节来处理 ,而一个汉字,是3个字节,因此就会出现乱码
	// 解决方法是 将  string 转成 []rune 即可, 因为 []rune是按字符处理,兼容汉字

	arr1 := []rune(str) 
	arr1[0] = '北'
	str = string(arr1)
	fmt.Println("str=", str)
}

二维数组

package main
import (
	"fmt"
)

func main() {
	//二维数组的演示案例
	/*
	0 0 0 0 0 0
	0 0 1 0 0 0
	0 2 0 3 0 0
	0 0 0 0 0 0
	*/

	//定义/声明二维数组
	var arr [4][6]int
	//赋初值
	arr[1][2] = 1
	arr[2][1] = 2
	arr[2][3] = 3

	//遍历二维数组,按照要求输出图形
	for i := 0; i < 4; i++ {
		for j := 0; j < 6; j++ {
			fmt.Print(arr[i][j], " ")
		}
		fmt.Println()
	}
	
	fmt.Println()
	
	var arr2 [2][3]int //以这个为例来分析arr2在内存的布局!!
	arr2[1][1] = 10
	fmt.Println(arr2)

	fmt.Printf("arr2[0]的地址%p\n", &arr2[0])
	fmt.Printf("arr2[1]的地址%p\n", &arr2[1])

	fmt.Printf("arr2[0][0]的地址%p\n", &arr2[0][0])
	fmt.Printf("arr2[1][0]的地址%p\n", &arr2[1][0])

	fmt.Println()
	arr3  := [2][3]int{{1,2,3}, {4,5,6}}
	fmt.Println("arr3=", arr3)

}

冒泡排序

package main
import (
	"fmt"
)

//冒泡排序
func BubbleSort(arr *[5]int) {

	fmt.Println("排序前arr=", (*arr))
	temp := 0 //临时变量(用于做交换)


	//冒泡排序..一步一步推导出来的
	for i :=0; i < len(*arr) - 1; i++ {
		
		for j := 0; j < len(*arr) - 1 - i; j++ {
			if (*arr)[j] > (*arr)[j + 1] {
				//交换
				temp = (*arr)[j]
				(*arr)[j] = (*arr)[j + 1]
				(*arr)[j + 1] = temp
			}
		}

	}

	
	fmt.Println("排序后arr=", (*arr))

}

func main() {

	//定义数组
	arr := [5]int{24,69,80,57,13}
	//将数组传递给一个函数,完成排序

	BubbleSort(&arr)

	fmt.Println("main arr=", arr) //有序? 是有序的
}

二分查找


package main
import (
	"fmt"
)

//二分查找的函数
/*
二分查找的思路: 比如我们要查找的数是 findVal
1. arr是一个有序数组,并且是从小到大排序
2.  先找到 中间的下标 middle = (leftIndex + rightIndex) / 2, 然后让 中间下标的值和findVal进行比较
2.1 如果 arr[middle] > findVal ,  就应该向  leftIndex ---- (middle - 1)
2.2 如果 arr[middle] < findVal ,  就应该向  middel+1---- rightIndex
2.3 如果 arr[middle] == findVal , 就找到
2.4 上面的2.1 2.2 2.3 的逻辑会递归执行
3. 想一下,怎么样的情况下,就说明找不到[分析出退出递归的条件!!]
if  leftIndex > rightIndex {
   // 找不到..
   return ..
}
*/
func BinaryFind(arr *[6]int, leftIndex int, rightIndex int, findVal int) {

	//判断leftIndex 是否大于 rightIndex
	if leftIndex > rightIndex {
		fmt.Println("找不到")
		return
	}

	//先找到 中间的下标
	middle := (leftIndex + rightIndex) / 2

	if (*arr)[middle] > findVal {
		//说明我们要查找的数,应该在  leftIndex --- middel-1
		BinaryFind(arr, leftIndex, middle - 1, findVal)
	} else if (*arr)[middle] < findVal {
		//说明我们要查找的数,应该在  middel+1 --- rightIndex
		BinaryFind(arr, middle + 1, rightIndex, findVal)
	} else {
		//找到了
		fmt.Printf("找到了,下标为%v \n", middle)
	}
}

func main() {

	arr := [6]int{1,8, 10, 89, 1000, 1234}

	//测试一把
	BinaryFind(&arr, 0, len(arr) - 1, -6)

}

map

package main
import (
	"fmt"
)

func main() {
	//第一种使用方式
	
	var a map[string]string
	//在使用map前,需要先make , make的作用就是给map分配数据空间
	a = make(map[string]string, 10)
	a["no1"] = "宋江" //ok?
	a["no2"] = "吴用" //ok?
	a["no1"] = "武松" //ok?
	a["no3"] = "吴用" //ok?
	fmt.Println(a)

	//第二种方式
	cities := make(map[string]string)
	cities["no1"] = "北京"
	cities["no2"] = "天津"
	cities["no3"] = "上海"
	fmt.Println(cities)

	//第三种方式
	heroes := map[string]string{
		"hero1" : "宋江",
		"hero2" : "卢俊义",
		"hero3" : "吴用",
	}
	heroes["hero4"] = "林冲"
	fmt.Println("heroes=", heroes)

	//演示删除
	delete(cities, "no1")
	fmt.Println(cities)
	//当delete指定的key不存在时,删除不会操作,也不会报错
	delete(cities, "no4")
	fmt.Println(cities)

	//演示map的查找
	val, ok := cities["no2"]
	if ok {
		fmt.Printf("有no1 key 值为%v\n", val)
	} else {
		fmt.Printf("没有no1 key\n")
	}

	//如果希望一次性删除所有的key
	//1. 遍历所有的key,如何逐一删除 [遍历]
	//2. 直接make一个新的空间
	cities = make(map[string]string)
	fmt.Println(cities)

	//遍历
	for k, v := range cities {
		fmt.Printf("k=%v v=%v\n", k, v)
	}

	//案例
	/*
	课堂练习:演示一个key-value 的value是map的案例
	比如:我们要存放3个学生信息, 每个学生有 name和sex 信息
	思路:   map[string]map[string]string

	*/
	studentMap := make(map[string]map[string]string)
	
	studentMap["stu01"] =  make(map[string]string, 3)
	studentMap["stu01"]["name"] = "tom"
	studentMap["stu01"]["sex"] = "男"
	studentMap["stu01"]["address"] = "北京长安街~"

	studentMap["stu02"] =  make(map[string]string, 3) //这句话不能少!!
	studentMap["stu02"]["name"] = "mary"
	studentMap["stu02"]["sex"] = "女"
	studentMap["stu02"]["address"] = "上海黄浦江~"

	fmt.Println(studentMap)
	fmt.Println(studentMap["stu02"])

	//遍历
	for k1, v1 := range studentMap {
		fmt.Println("k1=", k1)
		for k2, v2 := range v1 {
				fmt.Printf("\t k2=%v v2=%v\n", k2, v2)
		}
		fmt.Println()
	}
}

map2

package main
import (
	"fmt"
)

func main() {
	//演示map切片的使用
	/*
	要求:使用一个map来记录monster的信息 name 和 age, 也就是说一个
	monster对应一个map,并且妖怪的个数可以动态的增加=>map切片
	*/
	//1. 声明一个map切片
	var monsters []map[string]string
	monsters = make([]map[string]string, 2) //准备放入两个妖怪
	//2. 增加第一个妖怪的信息
	if monsters[0] == nil {
		monsters[0] = make(map[string]string, 2)
		monsters[0]["name"] = "牛魔王"
		monsters[0]["age"] = "500"
	}

	if monsters[1] == nil {
		monsters[1] = make(map[string]string, 2)
		monsters[1]["name"] = "玉兔精"
		monsters[1]["age"] = "400"
	}

	// 下面这个写法越界。
	// if monsters[2] == nil {
	// 	monsters[2] = make(map[string]string, 2)
	// 	monsters[2]["name"] = "狐狸精"
	// 	monsters[2]["age"] = "300"
	// }

	//这里我们需要使用到切片的append函数,可以动态的增加monster
	//1. 先定义个monster信息
	newMonster := map[string]string{
		"name" : "新的妖怪~火云邪神",
		"age" : "200",
	}
	monsters = append(monsters, newMonster)

	fmt.Println(monsters)
}

map3

package main
import (
	"fmt"
)
func modify(map1 map[int]int) {
	map1[10] = 900
}

//定义一个学生结构体
type Stu struct {
	Name string
	Age int
	Address string
}

func main() {

	//map是引用类型,遵守引用类型传递的机制,在一个函数接收map,
	//修改后,会直接修改原来的map

	map1 := make(map[int]int, 2)
	map1[1] = 90
	map1[2] = 88
	map1[10] = 1
	map1[20] = 2
	modify(map1)
	// 看看结果, map1[10] = 900 ,说明map是引用类型
	fmt.Println(map1) 


	//map的value 也经常使用struct 类型,
	//更适合管理复杂的数据(比前面value是一个map更好),
	//比如value为 Student结构体 【案例演示,因为还没有学结构体,体验一下即可】
	//1.map 的 key 为 学生的学号,是唯一的
	//2.map 的 value为结构体,包含学生的 名字,年龄, 地址

	students := make(map[string]Stu, 10)
	//创建2个学生
	stu1 := Stu{"tom", 18, "北京"}
	stu2 := Stu{"mary", 28, "上海"}
	students["no1"] = stu1
	students["no2"] = stu2

	fmt.Println(students)

	//遍历各个学生信息
	for k, v := range students {
		fmt.Printf("学生的编号是%v \n", k)
		fmt.Printf("学生的名字是%v \n", v.Name)
		fmt.Printf("学生的年龄是%v \n", v.Age)
		fmt.Printf("学生的地址是%v \n", v.Address)
		fmt.Println()
	}

}

你可能感兴趣的:(Go)