这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
一、函数
package main
import "fmt"
func add(a int, b int) int {
return a + b
}
func add2(a, b int) int {
return a + b;
}
func exists(m map[string]string, k string) (v string, ok bool) {
v, ok = m[k]
return v, ok
}
func main() {
res := add(1, 2)
fmt.Println(res)
v, ok := exists(map[string]string{"a": "A"}, "a")
fmt.Println(v, ok);
}
Go 语言函数定义格式如下:
func function_name( [parameter list] ) [return_types] {
函数体
}
函数定义解析:
func:函数由func开始声明function_name:函数名称,参数列表和返回值类型构成了函数签名。parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。return_types:返回类型,函数返回一列值。return_types是该列值的数据类型。有些功能不需要返回值,这种情况下return_types不是必须的。- 函数体:函数定义的代码集合。
与其他语言不同的是,go语言可以有多个返回值。
两种传参方式:
- 值传递:是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。
- 引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
函数的四种形式
- 第一种方式:有参有返回值
func test1(a int, b int)(int) {
fmt.Println(a + b) return a+b
}
func test2(a int, b int)(int,int) {
fmt.Println(a + b) return a+b,b*a
}
- 第二种方式:有参无返回值
func test3(a int, b int){
fmt.Println(a + b)
}
- 第三种方式:无参有返回值
func test4()(int,int){
a,b:=100,200 return a+b,a*b
}
- 第四种方式:无参无返回值
func test5() {
a, b := 100, 200
fmt.Println(a + b)
}
二、数组
package main
import "fmt"
func main() {
var a [5]int
a[4] = 100
fmt.Println(a[4], len(a))
b := [5]int{1, 2, 3, 4, 5}
fmt.Println(b)
var twoD [2][3]int
for i := 0; i < 2; i ++ {
for j := 0; j < 3; j ++ {
twoD[i][j] = i + j
}
}
fmt.Println("2d: ", twoD)
}
数组初始化
- 方式一:
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
- 方式二:
balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
- 如果数组长度不确定,可以使用
...代替数组的长度,编译器会根据元素个数自行推断数组的长度,经测试...也是可以省略的(类比C语言):
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
或
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
- 如果设置了数组的长度,我们还可以通过指定下标来初始化元素:
// 将索引为 1 和 3 的元素初始化
balance := [5]float32{1:2.0,3:7.0}
初始化数组中 {} 中的元素个数不能大于 [] 中的数字。
三、指针
package main
import "fmt"
func add2(n int) {
n += 2
}
func add2ptr(n *int) {
*n += 2
}
func main() {
n := 5
add2(n)
fmt.Println(n)
add2ptr(&n)
fmt.Println(n)
}
指针这块跟C语言的一样,如果你已经会C语言的指针,这部分可以跳过。
什么是指针
- 一个指针变量指向了一个值的内存地址。
指针声明格式:
var var_name *var-type
//var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。以下是有效的指针声明:
举例:
var ip *int /* 指向整型*/
var fp *float32 /* 指向浮点型 */
指针使用流程:
- 定义指针变量。
- 为指针变量赋值。
- 访问指针变量中指向地址的值。
在指针类型前面加上 * 号(前缀)来获取指针所指向的内容。
Go空指针
当一个指针被定义后没有分配到任何变量时,它的值为 nil。
nil 指针也称为空指针。
nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
一个指针变量通常缩写为 ptr。
空指针判断:
if(ptr != nil) /* ptr 不是空指针 */
if(ptr == nil) /* ptr 是空指针 */
既然Go语言有指针,哪是不是也有C语言那些恐怖的东西,答案是肯定的,什么指针的指针、指针数组、数组指针等等一些东西,感兴趣的同学可以自行了解,这里不做介绍。
四、结构体
package main
import "fmt"
type user struct {
name string
password string
}
func main() {
a := user{name: "wang", password: "1024"}
b := user{"wang", "1024"}
c := user{name: "wang"}
c.password = "1024"
var d user
d.name = "wang"
d.password = "1024"
fmt.Println(a, b, c, d)
fmt.Println(checkPassword(a, "haha"))
fmt.Println(checkPassword2(&a, "haha"))
}
func checkPassword(u user, password string) bool {
return u.password == password
}
func checkPassword2(u *user, password string) bool {
return u.password == password
}
结构体的定义:
Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
结构体表示一项记录,比如保存图书馆的书籍记录,每本书有以下属性:
- Title :标题
- Author : 作者
- Subject:学科
- ID:书籍ID
定义结构体
结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:
type struct_variable_type struct {
member definition
member definition
...
member definition
}
声明结构体:
variable_name := structure_variable_type {value1, value2...valuen}
或
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
访问结构体成员
如果要访问结构体成员,需要使用点号 . 操作符,格式为:
结构体.成员名
五、切片(Slice)
package main
import "fmt"
func main() {
s := make([]string, 3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("get:", s[2]) //c
fmt.Println("len:", len(s)) //3
s = append(s, "d")
s = append(s, "e", "f")
fmt.Println(s) //[a b c d e f]
c := make([]string, len(s))
copy(c, s)
fmt.Println(c) //[a b c d e f]
fmt.Println(s[2:5]) //[c d e]
fmt.Println(s[:5]) //[a b c d e]
fmt.Println(s[2:]) //[c d e f]
good := []string{"g", "o", "o", "d"}
fmt.Println(good) //[g o o d]
}
切片的定义
Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
切片的定义方式
- 方式一:你可以声明一个未指定大小的数组来定义切片:
var identifier []type
- 方式二:使用 make() 函数来创建切片:
var slice1 []type = make([]type, len)
也可以简写为
slice1 := make([]type, len)
也可以指定容量,其中 capacity 为可选参数。
make([]T, length, capacity)
其中,len 是数组的长度并且也是切片的初始长度。
切片初始化
- 第一种初始化方式:直接初始化切片,
[]表示是切片类型,{1,2,3}初始化值依次是1,2,3,其cap=len=3。看着跟数组一模一样,因为切片本质就是变成数组。
s :=[] int {1,2,3 }
- 第二种初始化方式:初始化切片
s,是数组arr的引用。
s := arr[:]
- 第三种初始化方式:将
arr中从下标startIndex到endIndex-1下的元素创建为一个新的切片。
s := arr[startIndex:endIndex]
- 第四种初始化方式:默认
endIndex时将表示一直到arr的最后一个元素。
s := arr[startIndex:]
- 第五种方式:默认
startIndex时将表示从arr的第一个元素开始。
s := arr[:endIndex]
- 第六种:通过切片
s初始化切片s1。
s1 := s[startIndex:endIndex]
- 第7种:通过内置函数
make()初始化切片**s**,[]int标识为其元素类型为int的切片。
s :=make([]int,len,cap)
len()和cap()函数
切片是可索引的,并且可以由 len() 方法获取长度。
切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。
空(nil)切片
一个切片在未初始化之前默认为 nil,长度为 0。
切片截取
可以通过设置下限及上限来设置截取切片 [lower-bound:upper-bound],区间为左闭右开。
append()和copy()函数
append()函数的作用是在切片后面追加元素。copy()函数是用来拷贝切片。
六、范围(range)
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
sum := 0
for i, num := range nums {
sum += num
if num == 2 {
fmt.Println("index:", i, "num:", num) //index: 0 num: 2
}
}
fmt.Println(sum) //9
m := map[string]string{"a" : "A", "b" : "B"}
for k, v := range m {
fmt.Println(k, v) //b 8; a A
}
for k := range m {
fmt.Println("key", k) //key a; key b
}
}
Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。
for循环的range格式可以对slice、map、数组、字符串等进行迭代循环。 格式如下:
for key, value := range oldMap {
newMap[key] = value
}
以上代码中的 key 和 value 是可以省略。
- 如果只想读取 key,格式如下:
for key := range oldMap
或者这样:
for key, _ := range oldMap
- 如果只想读取 value,格式如下:
for _, value := range oldMap
七、map
package main
import "fmt"
func main() {
m := make(map[string]int)
m["one"] = 1
m["two"] = 2
fmt.Println(m) // map[one:1 two:2]
fmt.Println(len(m)) // 2
fmt.Println(m["one"]) // 1
fmt.Println(m["unknow"]) // 0
//查看元素在集合中是否存在
r, ok := m["unknow"]
fmt.Println(r, ok) // 0 false
delete(m, "one")
m2 := map[string]int{"one" : 1, "two" : 2}
var m3 = map[string]int{"one" : 1, "two" : 2}
fmt.Println(m2, m3)
}
Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据。
定义map
- 可以使用内建函数
make也可以使用map关键字来定义Map:
/* 声明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type
/* 使用 make 函数 */
map_variable := make(map[key_data_type]value_data_type)
如果不初始化 map,那么就会创建一个 nil map。nil map 不能用来存放键值对
delete()函数
delete()函数用于删除集合的元素, 参数为map和其对应的key。