go(01) 基础语法
索引 1. 数据类型 2.变量 3.结构体 4.运算符 5.条件 6.循环 7.函数 8.数组 9.指针 10.切片 11.Range(范围) 12.Map 13.递归 14.类型转换 15.接口 16.并发 17.错误及异常
go的语句结尾不需要分号(;) 遇到换行就代表一个语句的结束
- bool
- string
- int、int8、int16、int32、int64
- uint、uint8、uint16、uint32、uint64、uintptr
- byte // uint8 的别名
- rune // int32 的别名 代表一个 Unicode 码
- float32、float64
- complex64、complex128
变量声明的标准格式: var 变量名 变量类型 变量声明以关键字 var 开头,后置变量类型,行尾无须分号
批量格式:
var ( a int b string c []int)
简短格式: 名字 := 表达式 如:
func main() { x:=100 a,s:=1, "abc" }
需要注意的是,简短模式(short variable declaration)有以下限制:
- 定义变量,同时显式初始化。
- 不能提供数据类型。
- 只能用在函数内部。
因为简洁和灵活的特点,简短变量声明被广泛用于大部分的局部变量的声明和初始化。var 形式的声明语句往往是用于需要显式指定变量类型地方,或者因为变量稍后会被重新赋值而初始值无关紧要的地方。
:= 只能在函数里使用,在全局无法编译, 所以一般使用var的方式来定义全局变量
注意:由于使用了 :=
,而不是赋值的 =
,因此推导声明写法的左值变量必须是没有定义过的变量。若定义过,将会发生编译错误。
no new variables on left side of := 在“:=”的左边没有新变量出现,意思就是“:=”的左边变量已经被声明了。
但是 = 还是可以的,因为这是为变量赋值而非定义
多变量声明 vname1, vname2, vname3 = v1, v2, v3
变量的初始化标准格式 var 变量名 类型 = 表达式 比如 var hp int = 100
编辑器推到类型的格式 var hp = 100 在标准格式的基础上,将 int 省略后,编译器会尝试根据等号右边的表达式推导 hp 变量的类型。
空标识符 如果一个字段在代码中从来不会被用到,那可以把它命名为 _,即空标识符
它可以用来避免为某个变量起名 同时也可以在赋值时 舍弃某个值
在函数中的 使用
package main import "fmt" func main() { _,numb,strs := numbers() //只获取函数返回值的后两个 fmt.Println(numb,strs) } //一个可以返回多个值的函数 func numbers()(int,int,string){ a , b , c := 1 , 2 , "str" return a,b,c } 输出结果: 2 str
3.结构体的定义 引自 : https://www.cnblogs.com/sparkdev/p/10761825.html
结构体的定义格式如下
type 类型名 struct {
字段1 字段1的类型
字段2 字段2的类型
…
}
结构体中的字段可以是任何类型,甚至是结构体本身,也可以是函数或者接口。可以声明结构体类型的一个变量,然后像下面这样给它的字段赋值:
type Person struct { 定义结构体类型 Name string Age int Email string }
var p Person 声明结构体 p.Name = "nick" 给结构体的成员变量赋值 p.Age = 28
结构体的初始化及访问方法
package main
import "fmt"
type Person struct{
Name string
Age int
}
func main() {
person := Person {"李四",18}
fmt.Printf("%s , %d\n",person.Name , person.Age)
}
package main import "fmt" type Person struct{ Name string Age int } func main() { person := Person {"李四",18} printPerson(person) } func printPerson (person Person){ fmt.Printf("%s , %d\n",person.Name , person.Age) }
package main import "fmt" type Person struct{ Name string Age int } func main() { person := Person {"李四",18} printPerson(&person) } func printPerson (person *Person){ fmt.Printf("%s , %d\n",person.Name , person.Age)
4.GO的运算符和C没什么分别
package main import "fmt" func main() { /* 局部变量定义 */ var a int = 100; /* 判断布尔表达式 */ if a < 20 { /* 如果条件为 true 则执行以下语句 */ fmt.Printf("a 小于 20\n" ); } else { /* 如果条件为 false 则执行以下语句 */ fmt.Printf("a 不小于 20\n" ); } fmt.Printf("a 的值为 : %d\n", a); } 以上代码执行结果为: a 不小于 20 a 的值为 : 100
package main import "fmt" func main() { /* 定义局部变量 */ var grade string = "B" var marks int = 90 switch marks { case 90: grade = "A" case 80: grade = "B" case 50,60,70 : grade = "C" default: grade = "D" } switch { case grade == "A" : fmt.Printf("优秀!\n" ) case grade == "B", grade == "C" : fmt.Printf("良好\n" ) case grade == "D" : fmt.Printf("及格\n" ) case grade == "F": fmt.Printf("不及格\n" ) default: fmt.Printf("差\n" ); } fmt.Printf("你的等级是 %s\n", grade ); } 以上代码执行结果为: 优秀! 你的等级是 A
使用 fallthrough 会强制执行后面的 case 语句,fallthrough 不会判断下一条 case 的表达式结果是否为 true。
package main import "fmt" func main() { switch { case false: fmt.Println("1、case 条件语句为 false") fallthrough case true: fmt.Println("2、case 条件语句为 true") fallthrough case false: fmt.Println("3、case 条件语句为 false") fallthrough case true: fmt.Println("4、case 条件语句为 true") case false: fmt.Println("5、case 条件语句为 false") fallthrough default: fmt.Println("6、默认 case") } } 以上代码执行结果为: 2、case 条件语句为 true 3、case 条件语句为 false 4、case 条件语句为 true
select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。
select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的。
以下描述了 select 语句的语法:
- 每个 case 都必须是一个通信
- 所有 channel 表达式都会被求值
- 所有被发送的表达式都会被求值
- 如果任意某个通信可以进行,它就执行,其他被忽略。
- 如果有多个 case 都可以运行,Select 会随机公平地选出一个执行。其他不会执行。
否则:- 如果有 default 子句,则执行该语句。
- 如果没有 default 子句,select 将阻塞,直到某个通信可以运行;Go 不会重新对 channel 或值进行求值。
package main import "fmt" func main() { var c1, c2, c3 chan int var i1, i2 int select { case i1 = <-c1: fmt.Printf("received ", i1, " from c1\n") case c2 <- i2: fmt.Printf("sent ", i2, " to c2\n") case i3, ok := (<-c3): // same as: i3, ok := <-c3 if ok { fmt.Printf("received ", i3, " from c3\n") } else { fmt.Printf("c3 is closed\n") } default: fmt.Printf("no communication\n") } } 以上代码执行结果为: no communication
Go 语言的 For 循环有 3 种形式,只有其中的一种使用分号。
和 C 语言的 for 一样:
for init; condition; post { }
和 C 的 while 一样:
for condition { }
和 C 的 for(;;) 一样:
for { }
- init: 一般为赋值表达式,给控制变量赋初值;
- condition: 关系表达式或逻辑表达式,循环控制条件;
- post: 一般为赋值表达式,给控制变量增量或减量。
具体实例参考 :https://www.runoob.com/go/go-loops.html
func [函数名] (函数形参,...) 返回类型 { 函数体 }
package main import "fmt" func main() { /* 定义局部变量 */ var a int = 100 var b int = 200 var ret int /* 调用函数并返回最大值 */ ret = max(a, b) fmt.Printf( "最大值是 : %d\n", ret ) } /* 函数返回两个数的最大值 */ func max(num1, num2 int) int { /* 定义局部变量 */ var result int if (num1 > num2) { result = num1 } else { result = num2 } return result } 以上实例在 main() 函数中调用 max()函数,执行结果为: 最大值是 : 200
package main import "fmt" func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap("Google", "Runoob") fmt.Println(a, b) } 以上实例执行结果为: Runoob Google
Go 语言可以很灵活的创建函数,并作为另外一个函数的实参。以下实例中我们在定义的函数中初始化一个变量,该函数仅仅是为了使用内置函数 math.sqrt(),实例为: 实例 package main import ( "fmt" "math" ) func main(){ /* 声明函数变量 */ getSquareRoot := func(x float64) float64 { return math.Sqrt(x) } /* 使用函数 */ fmt.Println(getSquareRoot(9)) } 以上代码执行结果为: 3
闭包的概念是 在函数A里创建 匿名函数B , 当A定义一个局部变量,一般情况下该变量会随着函数A的结束而释放, 但如果这时 B 调用了该变量, 函数A结束时该变量仍会存在于内存中, 当再次调用 A 时函数内部定义的局部变量 仍会是上一次调用的变量
闭包的作用 , 可以让临时变量在外部函数生命周期结束时,仍然存在于内存之中!
闭包的弊端 ,由于闭包会使函数中的变量保存在内存中,内存消耗很大,所以不能滥用闭包,解决办法是,退出函数之前,将不使用的局部变量删除。
package main import "fmt" func testfunc() func() int{ i:=0 return func() int{ i+=1 return i } } func main() { x := testfunc() //x是一个函数 因为:=的特殊 会根据右值来定义左值的类型 fmt.Println(x()) fmt.Println(x()) fmt.Println(x()) }
数组其实就是一个指向一块连续地址的指针
声明数组 var variable_name [SIZE] variable_type
初始化数组与访问的方法和C无异, 注意如果数组的value不是在声明时初始化, 那之后只能通过访问数组下标的方式赋值
package main import "fmt" func main() { var arr = [3]int {0,1,2} //也可 arr := [3]int {0,1,2} for i:=0;i<3;i++{ fmt.Printf("%d \n", arr[i]) } }
二维数组的定义及访问
package main import "fmt" func main() { var arr [3][4]int for i:= 0; i<3; i++{ for j:=0; j<4; j++{ arr[i][j] = j } } for i:= 0; i<3; i++{ for j:=0; j<4; j++{ fmt.Printf("%d\n",arr[i][j]) } } } 执行结果 0 1 2 3 0 1 2 3 0 1 2 3
向函数传递数组
func func_name (praams []int ){ ... } 不指明数组长度
func func_name (praams [len]int ){ ... } 指明数组长度
var array = []int{1, 2, 3, 4, 5} /* 未定义长度的数组只能传给不限制数组长度的函数 */ setArray(array) /* 定义了长度的数组只能传给限制了相同数组长度的函数 */ var array2 = [5]int{1, 2, 3, 4, 5}
定义指针 var var_name *var-type
指针使用和C的无异
package main import "fmt" func main() { a := 10 p:= &a fmt.Printf("指针指向的值%d\n指针本身的地址%x\n指针指向的地址%x\n",*p,p,&p) }
运行结构
指针指向的值10
指针本身的地址c0000160a0
指针指向的地址c00000e028
指针数组 var ptr [len]*int
package main import "fmt" const MAX int = 3 func main() { a := []int{10,100,200} var i int var ptr [MAX]*int; for i = 0; i < MAX; i++ { ptr[i] = &a[i] /* 整数地址赋值给指针数组 */ } for i = 0; i < MAX; i++ { fmt.Printf("a[%d] = %d\n", i,*ptr[i] ) } } 以上代码执行输出结果为: a[0] = 10 a[1] = 100 a[2] = 200
二级指针 var ptr1 *int a var ptr2 **int b b = &a
package main import "fmt" func main() { var a int var ptr *int var pptr **int a = 3000 /* 指针 ptr 地址 */ ptr = &a /* 指向指针 ptr 地址 */ pptr = &ptr /* 获取 pptr 的值 */ fmt.Printf("变量 a = %d\n", a ) fmt.Printf("指针变量 *ptr = %d\n", *ptr ) fmt.Printf("指向指针的指针变量 **pptr = %d\n", **pptr) } 以上实例执行输出结果为: 变量 a = 3000 指针变量 *ptr = 3000 指向指针的指针变量 **pptr = 3000
指针作为函数的形参
package main import "fmt" func main() { /* 定义局部变量 */ var a int = 100 var b int= 200 fmt.Printf("交换前 a 的值 : %d\n", a ) fmt.Printf("交换前 b 的值 : %d\n", b ) /* 调用函数用于交换值 * &a 指向 a 变量的地址 * &b 指向 b 变量的地址 */ swap(&a, &b); fmt.Printf("交换后 a 的值 : %d\n", a ) fmt.Printf("交换后 b 的值 : %d\n", b ) } func swap(x *int, y *int) { var temp int temp = *x /* 保存 x 地址的值 */ *x = *y /* 将 y 赋值给 x */ *y = temp /* 将 temp 赋值给 y */ } 以上实例允许输出结果为: 交换前 a 的值 : 100 交换前 b 的值 : 200 交换后 a 的值 : 200 交换后 b 的值 : 100
package main import "fmt" func main() { /* 定义局部变量 */ var a int = 100 var b int= 200 swap(&a, &b); fmt.Printf("交换后 a 的值 : %d\n", a ) fmt.Printf("交换后 b 的值 : %d\n", b ) } /* 交换函数这样写更加简洁,也是 go 语言的特性,可以用下,c++ 和 c# 是不能这么干的 */ func swap(x *int, y *int){ *x, *y = *y, *x }
切片类似于动态数组, 长度可追加,
定义切片有三种:
1.声明一个未指定长度的数组 var identifier []type
2.通过内置函数make()创建切片 var slie_name [ ]type = make([ ]type , len , [cap])
[]type是数据类型 len是切片长度(长度不可大于容量否则报错) cap是切片容量(可选参数,如果不给则默认等于len)
3.通过引用已经存在的数组创建 var arr = [6]int {0,1,2,3,4,5}
slice := arr[start_index:end_index-1] 引用arr下标从start_index到end_index-1的的元素作为初始化切片的值
slice := arr[1:3] slice未指定长度所以是切片 , 引用arr数组的下标1到下标3-1的两个元素来初始化新创建的切片
s := arr[start_index:] 默认从start_index开始一直到数组的最后一个元素
s := arr[:end_index-1] 默认从第一个元素开始一直到指定的下标为end_index-1结束
如果创建切片时未初始化那么切片默认是空的, len=0 , cap=0 , slice=nil
切片有两个内置函数 len(slice) 和cap(slice) , len获取切片的长度 , cap获取切片的容量
append()和copy()
如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来
package main import "fmt" func main() { var numbers []int printSlice(numbers) /* 允许追加空切片 */ numbers = append(numbers, 0) printSlice(numbers) /* 向切片添加一个元素 */ numbers = append(numbers, 1) printSlice(numbers) /* 同时添加多个元素 */ numbers = append(numbers, 2,3,4) printSlice(numbers) /* 创建切片 numbers1 是之前切片的两倍容量*/ numbers1 := make([]int, len(numbers), (cap(numbers))*2) /* 拷贝 numbers 的内容到 numbers1 */ copy(numbers1,numbers) printSlice(numbers1) } func printSlice(x []int){ fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) } 以上代码执行输出结果为: len=0 cap=0 slice=[] len=1 cap=1 slice=[0] len=2 cap=2 slice=[0 1] len=5 cap=6 slice=[0 1 2 3 4] len=5 cap=12 slice=[0 1 2 3 4]
Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。
range关键字会返回两个值(索引和索引对应的值), 可以用 _(空标识符)来舍弃不需要的值
package main import "fmt" func main() { arr := []int {0,1,2,3,4} for n,v := range arr{ fmt.Println("index:",n) fmt.Println("value:",v) } }
执行结果
index: 0
value: 0
index: 1
value: 1
index: 2
value: 2
index: 3
value: 3
index: 4
value: 4
也可 用于map的遍历
package main
import "fmt"
func main() {
kvs := map[int]string{1:"hello",2:"world",3:"!"}
for k,v:= range kvs{
fmt.Printf("[%d]%s\n",k,v)
}
}
执行结果
[1]hello
[2]world
[3]!
声明map可以使用map关键字也可以使用内置函数make , 声明map默认是nil
var map_name map[key_type]value_type 只声明默认是nil
map_name := make([key_type]value_type)
map插入key和value
var map_name [int]string
map_name [1] = "hello"
map_name [2] = "world"
map_name [3] = "!"
定义map并初始化 type_map := map[int]string {1:"a",2:"b",3:"c"}
delete() 用于删除集合元素
delete(map_name,map_key) 删除指定map里的指定索引值代表的value
递归函
请发表评论