变量
//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 不区分大小写。
二进制转十进制
八进制转换成十进制
16 进制转成 10 进制
十进制如何转二进制
十进制转成八进制
十进制转十六进制
二进制转换成八进制
二进制转成十六进制
八进制转换成二进制
十六进制转成二进制
进制输出
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()
}
}