主要学习自:

https://golang.iswbm.com/c01/c01_06.html

1.变量声明

1.1.单一变量声明

一行声明一个变量:

1
var <name> <type>

例如:

1
2
3
var name string="go编程学习"

var name="go编程学习"

go存在隐式初始化,string 初始化为空字符串,int类型初始化为0,float初始化为0.0,bool类型初始化为false,指针类型初始化为nil。

1.2 多个变量一起声明

例如:

1
2
3
4
5
var{
name string
age int
gender string
}

1.3 声明和初始化一个变量

使用:=推导声写法(编译器会自动根据右值类型推断出左值的对应类型)。

1
2
3
4
5
name :="go编程"
// 等价于
var name string ="go编程"
// 等价于
var name="go编程"

1.4 声明和初始化多个变量

1
name,age:="zhangsan",28

1.5 new函数声明一个指针变量

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
ptr := new(int)
fmt.Println("ptr address: ", ptr)
fmt.Println("ptr value: ", *ptr) // * 后面接指针变量,表示从内存地址中取出值
}

ptr在这里为指针变量,指针变量存放的是地址,普通变量存放的是值。

所以输出:

1
2
ptr address: 0x1400001c0b8
ptr value 0

2.数据类型

2.1 整形,进制表示

1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main() {
var num01 int = 0b1100
var num02 int = 0o14
var num03 int = 0xC
fmt.Printf("2进制数%b 表示的是:%d \n\n", num01, num01)
fmt.Printf("8进制数%o 表示的是:%d \n\n", num02, num02)
fmt.Printf("16进制数%x 表示的是:%d \n\n", num03, num03)
}

输出:

1
2
3
4
5
2进制数1100 表示的是:12 

8进制数14 表示的是:12

16进制数c 表示的是:12

二进制以0b开头,格式化字符串中以%b表示。

八进制以0o开头,格式化字符串以%o表示

十六进制以0x开头,格式化字符串以%x表示。

fmt包的格式化功能:

1
2
3
4
5
6
7
8
9
10
%b    表示为二进制
%c 该值对应的unicode码值
%d 表示为十进制
%o 表示为八进制
%q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
%x 表示为十六进制,使用a-f
%X 表示为十六进制,使用A-F
%U 表示为Unicode格式:U+1234,等价于"U+%04X"
%E 用科学计数法表示
%f 用浮点数表示

2.2 byte,rune与字符串

Byte:占用1个字节,8个比特位(2^8,byte范围2-255)和uint8类型本质上没有区别,他表示的是ascii中的一个字符。

例如:

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
var a byte = 65
var b uint8 = 66
fmt.Printf("a 的值:%c \nb 的值:%c\n", a, b)
}

输出:

1
2
a 的值:A 
b 的值:B

Rune,占用4个字节,共32位比特位,所以和uint32本质上没有区别。它表示一个Unicode(Unicode是一个可以表示世界范围内的绝大部分自负的编码规范)

1
2
3
4
5
6
7
8
9
10
11
12
package main

import (
"fmt"
"unsafe"
)

func main() {
var a byte = 'A'
var b rune = 'B'
fmt.Printf("a 占用 %d 个字节数\nb 占用 %d 个字节数\n", unsafe.Sizeof(a), unsafe.Sizeof(b))
}

输出:

1
2
a 占用 1 个字节数
b 占用 4 个字节数

字符串:

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

func main() {
var mystr01 string = "hello"
var mystr02 [5]byte = [5]byte{104, 101, 108, 108, 111}
fmt.Printf("mystr01: %s\n", mystr01)
fmt.Printf("mystr02: %s", mystr02)
}

输出:

1
2
mystr01: hello
mystr02: hello

mystr01 和 mystr02 输出一样,说明了 string 的本质,其实是一个 byte数组

注:字符串除了使用双引号包裹(””),还可以使用反引号(``)

使用反引号包裹的字符相当于python中的row字符串,会忽略里面的转义。

2.3 数组与切片

1.数组的三种写法:

1
2
3
4
5
6
7
8
9
// 第一种写法
var arr [3]int =[3]int{1,2,3}

// 第二种写法
arr :=[3]int{1,2,3}

// 第三种写法
// 以后还想往数组中增加元素,为了避免硬编码,可以使用...让go语言根据实际情况来分配空间
arr :=[...]int{1,2,3}

例如:

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

func main() {
arr01 := [...]int{1, 2, 3}
arr02 := [...]int{1, 2, 3, 4}
fmt.Printf("%d 的类型是:%T\n", arr01, arr01)
fmt.Printf("%d 的类型是:%T", arr02, arr02)
}

输出:

1
2
[1 2 3] 的类型是:[3]int
[1 2 3 4] 的类型是:[4]int

2.切片:切片(Slice)与数组一样,也是可以容纳若干类型相同的元素的容器。与数组不同的是,无法通过切片类型来确定其值的长度。每个切片值都会将数组作为其底层数据结构。我们也把这样的数组称为切片的底层数组。

1
2
3
4
5
6
7
8
package main

import "fmt"

func main() {
myarr := [...]int{1, 2, 3}
fmt.Printf("%d 的类型是:%T", myarr[0:2], myarr[0:2])
}

输出:

1
[1 2] 的类型是:[]int

3.使用make函数构造,make函数的格式:make([]Type,size,cap)

这个函数刚好指出了,一个切片具备的三个要素:类型(type),长度(size),容量(cap)

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
a := make([]int, 2)
b := make([]int, 2, 10)
fmt.Println(a, b)
fmt.Println(len(a), cap(b))
fmt.Println(cap(a), cap(b))
}

输出:

1
2
3
[0 0] [0 0]
2 10
2 10

2.4 字典与布尔类型

字典

字典是有若干个key:value这样的键值对映射组合在一起的数据结构。

它是hash表的一个实现,这就要求它的映射里的key,都是唯一的,可以使用==和!=来进行判等操作,换句话说就是key必须是可hash的

可hash,简单来说,一个不可变对象,都可以用一个hash值来唯一表示,这样的不可变对象,比如字符串类型的对象(可以说除了切片,字典,函数之外的其他内建类型都算)

意思就是,你的 key 不能是切片,不能是字典,不能是函数。

字典由key和value组成,它们各自有各自的类型。

声明初始化字典:
1
2
3
4
5
6
7
8
9
10
//第一种写法
var scores map[string]int=map[string]int{"englist":80,"chinese":85}

//第二种方法
scores :=map[string]int{"english":80,"chinese":85}

//第三种方法
scores := make(map[string]int)
scores["english"]=80
scorees["chinese"]=85

第一种赋值方法拆分(声明,初始化,再赋值),相对来说会麻烦很多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

func main() {
//声明一个名为score的字典
var scores map[string]int

// 为初始化的score 的零值为nil,无法直接进行赋值
if scores == nil {
//需要使用make函数对其初始化
scores = make(map[string]int)
}
//经过初始化后,才可以直接赋值
scores["chinese"] = 90
fmt.Println(scores)
}
字典相关操作:

添加元素:

1
scores["math"]=95

更新元素:

1
scores["math"]=90

删除元素:

1
delete(scores,"math")
判断key是否存在:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
scores := map[string]int{"english": 80, "chinese": 85}
// scores回返回两个值,第二个值表示对应的key是否存在,若key为true,则存在
math, ok := scores["math"]
if ok {
fmt.Printf("math 的值是: %d\n", math)
} else {
fmt.Println("math不存在")
}
}
对字典进行循环:
1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

func main() {
scores := map[string]int{"english": 80, "chinese": 85}
for subject, scores := range scores {
fmt.Printf("key: %s,value: %d\n\n", subject, scores)
}
}

布尔:

在go中,真值用true表示,不但不与1相等,并且更加严格,不同类型无法进行比较,而假值用false表示,同样与0无法比较。

如果要bool与int转换,需要自己实现函数。

bool转int

1
2
3
4
5
6
func bool2int(b bool) int{
if b{
return 1
}
renturn 0
}

Int 转bool

1
2
3
func int2bool(i int) bool{
return i !=0
}
取反:

go使用!进行取反,和python not 取反有区别

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

var male bool = true

func main() {
fmt.Println(!male == false)
fmt.Println(male != false)
}
且/或:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

var age int = 15
var gender string = "main"

func main() {
// && 两边的表达式都会执行
fmt.Println(age > 18 && gender == "male")
// gender == "male" 并不会执行.短路行为,左边表达式执行,右边就不再执行
fmt.Println(age < 18 || gender == "male")

}

2.5 指针

根据变量指向的值,是否存在内存地址,变量分为两种:

  • 普通变量 :存数据本身
  • 指针变量 :存值的内存地址

1.指针的创建

第一种:

定义对应的变量,再通过变量取得内存地址,创建指针

1
2
3
4
//定义普通变量
aint :=1
//定义指针变量
ptr :=&ant

第二种:

先创建指针,分配好内存,再给指针指向的内存地址写入对应的值

1
2
3
4
//创建指针
astr :=new(string)
//给指针赋值
*astr="go编程时光"

第三种:
先声明一个指针变量,再从其他变量取得内存地址赋值给它

1
2
3
aint :=1
var bint *int //声明一个指针
bint = &aint //初始化

指针的操作都离不开这两个符号:

  • &:从一个普通变量中取得内存地址
  • :当在赋值操作符(=)的右边,是从一个指针变量中取得变量值,当*在赋值操作符(=)的左边,是指该指针指向的变量
1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main() {
ant := 1
ptr := &ant
fmt.Println("普通变量存储的是:", ant)
fmt.Println("普通变量存储的是:", *ptr)
fmt.Println("普通变量存储的是:", &ant)
fmt.Println("普通变量存储的是:", ptr)
}

输出:

1
2
3
4
普通变量存储的是: 1
普通变量存储的是: 1
普通变量存储的是: 0x1400001c0b0
普通变量存储的是: 0x1400001c0b0

2.指针的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

func main() {
astr := "hello"
aint := 1
abool := false
arune := 'a'
afloat := 1.2

fmt.Printf("astr 指针类型是:%T\n", &astr)
fmt.Printf("aint 指针类型是:%T\n", &aint)
fmt.Printf("abool 指针类型是:%T\n", &abool)
fmt.Printf("arune 指针类型是:%T\n", &arune)
fmt.Printf("afloat 指针类型是:%T\n", &afloat)
}

输出:

1
2
3
4
5
astr 指针类型是:*string
aint 指针类型是:*int
abool 指针类型是:*bool
arune 指针类型是:*int32
afloat 指针类型是:*float64

*+所指向变量值的数据类型,就是对应的指针类型。

若定义一个直接收指针类型的参数的函数,可以这么写:

1
2
3
func mytest(ptr *int)  {
fmt.Println(*ptr)
}

3.控制流程

go里的流程控制方法还是很丰富的,整理如下:

1
2
3
4
5
6
7
8
9
if - else 条件语句

switch - case 选择语句

for - range 循环语句

goto 无条件跳转语句

defer 延迟执行

3.1控制流程if-else

go编译器,对{,}的位置有严格的要求,他要求else if和两边的花括号,必须在同一行。

go是强类型,所以要求条件表达式必须严格返回布尔类型的数据(nil和0和1都不行)

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
age := 20
if age > 18 {
fmt.Println("已成年")
} else if age > 12 {
fmt.Println("已经是青年")
} else {
fmt.Println("还不是青年")
}
}

高级写法:

if里可以先运行一个表达式,取得变量后,在对其进行判断,比如:

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
if age:=20;age>18{
fmt.Println("已成年")
}
}

3.2switch-else

Switch-else语句模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
switch 表达式 {
case 表达式1:
代码块
case 表达式2:
代码块
case 表达式3:
代码块
case 表达式4:
代码块
case 表达式5:
代码块
default:
代码块
}

一个case多个条件:

case后可接多个条件,多个条件之间是的关系,用逗号分隔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import "fmt"

func main() {
month := 2

switch month {
case 3, 4, 5:
fmt.Println("春天")
case 6, 7, 8:
fmt.Println("夏天")
case 9, 10, 11:
fmt.Println("秋天")
case 12, 1, 2:
fmt.Println("冬天")
default:
fmt.Println("输入有误...")
}
}

switch后可接函数

switch后面可以接一个函数,只要保证case后的值类型与函数的返回值一致即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import "fmt"

// 判断一个同学是否有挂科记录的函数
// 返回值是布尔类型
func getResult(args ...int) bool {
for _, i := range args {
if i < 60 {
return false
}
}
return true
}

func main() {
chinese := 80
english := 50
math := 100

switch getResult(chinese, english, math) {
// case 后也必须 是布尔类型
case true:
fmt.Println("该同学所有成绩都合格")
case false:
fmt.Println("该同学有挂科记录")
}
}

3.for循环

for循环模型:

1
2
3
4
for [condition |  ( init; condition; increment ) | Range]
{
statement(s);
}

例如:

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
for i := 1; i <= 5; i++ {
fmt.Println(i)
}
}

不接表达式:无限循环

在go语言中,没有while循环,如果要实现无限循环,可以使用for实现

当不加任何判断条件时,相当于每次的判断都为true,程序一直处于运行状态

再满足一定的条件下可以使用关键字break退出循环体,也可以使用continue直接跳入下一循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
var i int = 1
for {
if i > 5 {
break
}
fmt.Printf("hello,%d\n", i)
i++
}
}

for-range语句

range后可接数组,切片,字符串

range会返回两个值:索引和数据,若后面用不到索引需要使用_表示

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
myarr := [...]string{"world", "python", "go"}
// 第一个值表示key,第二个值为value
for _, item := range myarr {
fmt.Printf("hello,%s\n", item)
}
}

输出如下:

1
2
3
hello, world
hello, python
hello, go

如果用一个变量来接收的话,接收到是索引:

1
2
3
4
5
6
7
8
import "fmt"

func main() {
myarr := [...]string{"world", "python", "go"}
for i := range myarr {
fmt.Printf("hello, %v\n", i)
}
}

输出:

1
2
3
hello, 0
hello, 1
hello, 2