说明:笔记内容参考Go指南
目录
二、流程控制语句:for、if、else、swith、defer
一、包、变量和函数
1.运行入口
Go程序由包组成,程序的运行入口是main。
package main
2.导入路径
通过圆括号组合导入,即“打包”导入语句,如:
import (
"fmt"
"math"
)
同时也可以分组导入(形式上更好)
import "fmt"
import "math"
3.简单的输出
如果一个名字以大写字母开头,如Pi,表明它是已导出的,导出自math包
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.Pi)
}
4.格式化输入输出
[布尔]
%t
[整数]
%b 二进制表示
%d 十进制表示
%o 八进制表示
%x 十六进制表示,字母形式为小写 a-f
%X 十六进制表示,字母形式为大写 A-F
%c 相应 Unicode 码点所表示的字符
[字符串与字节切片]
%s 字符串或切片的无解译字节
%q 双引号围绕的字符串,由 Go 语法安全地转义
[指针]
%p
5.函数
可以没有参数,也可以接受多个参数
连续多个函数命名形参类型相同时,除最后一个都可以省略
如:
x int, y int
可以所写成
x, y int
func swap(x, y string) (string, string) {
return y, x
}
func add(x, y int) (int, int, int) {
return x + y, x, y
}
func main() {
fmt.Println("Hello World!")
fmt.Println(add(1, 4))
fmt.Println(swap("hello", "world"))
}
6.返回值
go可返回任意多个返回值
且返回值可以被命名,名称应当具有一定的意义,return可以直接返回已命名的返回值(不适用于长函数,影响代码可读性)
func jisuan(sum int) (x, y int) {
x = sum*2 + 1
y = sum - 1
return
}
func main() {
fmt.Println(jisuan(5))
}
7.变量申明
var定义变量列表
在函数内,可用:=代替var定义
函数外必须以关键字(var、func等)开始
var a, b int = 1, 2
func main() {
var x, y = "Be", "Happy"
c := 2 * 2
fmt.Println(a, b, x, y, c)
}
短变量声明
可以用:=代替var,但:=不能用于函数外
8.基本类型
布尔型 bool
字符串类型 string
数字类型
int8 (有符号 8 位整型 (-128 到 127) )
int16 (有符号 16 位整型 (-32768 到 32767) )
int32 (有符号 32 位整型 (-2147483648 到 2147483647) )
int64 (有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) )
unit8 (无符号 8 位整型 (0 到 255) )
unit16(无符号 16 位整型 (0 到 65535) )
unit32(无符号 32 位整型 (0 到 4294967295) )
unit64(无符号 64 位整型 (0 到 18446744073709551615) )
float32 (IEEE-754 32位浮点型数 )
float64 (IEEE-754 64位浮点型数 )
complex64 (32 位实数和虚数 )
complex128 (64 位实数和虚数 )
其他数字类型:
byte (uint8 的别名)
rune (int32 的别名, 表示一个 Unicode 码点)
int 有符号整型32 或 64 位
unit 无符号整型32 或 64 位
unitptr (无符号整型,用于存放一个指针。 uintptr类型只有在底层编程是才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方 )
派生类型
同导入语句一样, 变量声明也可以“分组”成一个语法块
%T显示类型 %v显示value
9.零值
没有明确初始值的变量声明会被赋予它们的零值。
数值型 0;布尔型 false;字符串型 “''(空字符串)
var i int
var f float32
var b bool
var s string
fmt.Printf("%v, %v, %v, %q\n", i, f, b, s)
注:需要格式化输出信息时用Printf,其他时间可以用Println
10.类型转换
表达式 T(v)将值v转换为类型T
与 C 不同的是,Go 在不同类型的项之间赋值时需要显式转换。
在声明一个变量而不指定其类型时,变量的类型由右值推导得出
func main() {
i := 4
f := 3.14
c := 0.6 + 5i
fmt.Printf("%v:%T\n", i, i)
fmt.Printf("%v:%T\n", f, f)
fmt.Printf("%v:%T\n", c, c)
}
运行结果
4:int
3.14:float64
(0.6+5i):complex128
11.常量
使用 const
关键字 ,可以是字符、字符串、布尔值或数值 ,不能用 :=
语法声明
func main() {
const i = 1
fmt.Println(i)
const a = 'a'
fmt.Printf("%c\t%d\t%v\n", a, a, a)
const name = "a"
fmt.Printf("%v\n", name)
}
运行结果
1
a 97 97
a
一个未指定类型的常量由上下文来决定其类型
二、流程控制语句:for、if、else、swith、defer
for循环
for 初始化语句;条件表达式;后置语句{ }
for i := 1; i < 10; i++ {
fmt.Println(i)
}
其中,初始化语句和后置语句是可选的 ,for ;条件表达式;{ }
var s int = 1
for ;s < 10; {
s += s
}
fmt.Println(s)
for 条件表达式等价于其他语言中的while
(发现一个问题: 在输入语句 for ;s < 10; 运行时会自动变成 for s < 10 )
如果省略循环条件,该循环就不会结束
if、else
package main
import "fmt"
func main() {
for i := 1; i < 10; i++ {
if i += 1; i%2 == 0 {
fmt.Println(i)
}
}
}
if
语句可以在条件表达式前执行一个简单的语句 ,该语句声明的变量作用域仅在 if
之内
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
} else {
fmt.Printf("%g >= %g\n", v, lim)
}
return lim
}
func main() {
fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
pow(4, 2, 10),
pow(2, 3, 20),
)
}
运行结果
27 >= 20
16 >= 10
9 20 10 8
注意输出的顺序
switch
自动提供了 case的 break
语句 ,且case 无需为常量,且取值不必为整数
func main() {
fmt.Println("put a number:")
var age int
// fmt.Scan(&age)
fmt.Scanf("%d", &age)
switch {
case age < 3:
fmt.Println("baby")
case age < 18:
fmt.Println("student")
case age > 60:
fmt.Println("old")
default:
fmt.Println("work hard")
}
}
case 语句从上到下顺次执行,直到匹配成功时停止
没有条件的 switch 同 switch true
一样
defer
defer将函数推迟到外层函数返回之后执行
推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用 ,推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用
func main() {
fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
运行结果
counting
done
9
8
7
6
5
4
3
2
1
0
注意输出的顺序
三、更多类型:struct、slice和映射
指针
指针保存了值的内存地址 ,类型 *T
是指向 T
类型值的指针
&
操作符会生成一个指向其操作数的指针
*
操作符表示指针指向的底层值
fmt.Println(*p) // 通过指针 p 读取 i
*p = 21 // 通过指针 p 设置 i
与 C 不同,Go 没有指针运算
func main() {
i := 7
j := 9
var p *int
p = &i //指向i
fmt.Println(i)
fmt.Println(*p) //通过指针读i的值
*p += 1 //通过指针修改i的值
fmt.Println(i)
fmt.Println(*p)
p = &j //指向j
*p *= 2 //通过指针修改j的值
fmt.Println(i)
fmt.Println(j)
fmt.Println(*p) //通过指针读j的值
}
运行结果
7
7
8
8
8
18
18
结构体
结构体(struct
)就是一个字段的集合 ,结构体字段使用点号或结构体指针来访问
func main() {
v := Vertex{1, 2}
fmt.Println(v) //{1 2}
p := &v
fmt.Println(*p) //{1 2}
p.X = 9
fmt.Println(p.X) //9
fmt.Println(p.Y) //2
fmt.Println(v) //{9 2}
}
结构体文法通过直接列出字段的值来新分配一个结构体
使用 Name:
语法可以仅列出部分字段。(字段名的顺序无关。)
特殊的前缀 &
返回一个指向结构体的指针。
type Vertex struct {
X, Y int
}
var (
v1 = Vertex{1, 2} // has type Vertex
v2 = Vertex{Y: 9} // X:0 is implicit
v3 = Vertex{} // X:0 and Y:0
p = &Vertex{1, 2} // has type *Vertex
)
func main() {
fmt.Println(v1, v2, v3) //{1 2} {0 9} {0 0}
fmt.Println(p) //&{1 2}
fmt.Println(*p) //{1 2}
}
数组
类型 [n]T
表示拥有 n
个 T
类型的值的数组
var a [10]int //变量 a 声明为拥有 10 个整数的数组
切片
类型 []T
表示一个元素类型为 T
的切片
a[low : high] //半开区间,包括第一个元素,但排除最后一个元素
更改切片的元素会修改其底层数组中对应的元素
t := [5]int{1,3,5,7,9}
fmt.Println(t) //[1 3 5 7 9]
tt := t[1:4]
fmt.Println(tt) //[3 5 7]
tt[2] = 8
fmt.Println(tt) //[3 5 8]
切片文法类似于没有长度的数组文法
func main() {
q := []int{2, 3, 5, 7, 11, 13}
fmt.Println(q)
r := []bool{true, false, true, true, false, true}
fmt.Println(r)
s := []struct {
i int
b bool
}{
{2, true},
{3, false},
{5, true},
{7, true},
{11, false},
{13, true},
}
fmt.Println(s)
}
运行结果
[2 3 5 7 11 13]
[true false true true false true]
[{2 true} {3 false} {5 true} {7 true} {11 false} {13 true}]
切片下界的默认值为 0
,上界则是该切片的长度
s := []int{2, 3, 5, 7, 11, 13}
t := s[1:4]
fmt.Println(t) //[3 5 7]
q := s[:2]
fmt.Println(q) //[2 3]
p := s[1:]
fmt.Println(p) //[3 5 7 11 13]
切片拥有 长度 和 容量
len(s)
获取长度:它所包含的元素个数
cap(s)
获取容量:从它的第一个元素开始数,到其底层数组元素末尾的个数
切片的零值是 nil
,nil 切片的长度和容量为 0 且没有底层数组
切片可以用内建函数 make
来创建
a := make([]int, 5) // len(a)=5
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
切片可包含任何类型,甚至包括其它的切片
package main
import (
"fmt"
"strings"
)
func main() {
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}
board[0][0] = "X"
board[2][2] = "O"
board[1][2] = "X"
board[1][0] = "O"
board[0][2] = "X"
fmt.Println(board)
for i := 0; i < len(board); i++ {
fmt.Printf("%s\n", strings.Join(board[i], " "))
}
}
运行结果
[[X _ X] [O _ X] [_ _ O]]
X _ X
O _ X
_ _ O
append
函数为切片追加新的元素
扩展阅读
range
for
循环的 range
形式可遍历切片或映射
当使用 for
循环遍历切片时,每次迭代都会返回两个值:下标和对应元素
可以将下标或值赋予 _
来忽略
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
for _, v := range pow {
fmt.Printf("%d\n",v)
}
}
运行结果
2**0 = 1
2**1 = 2
2**2 = 4
2**3 = 8
2**4 = 16
2**5 = 32
2**6 = 64
2**7 = 128
1
2
4
8
16
32
64
128
扩展阅读
映射
映射将键映射到值 ,是一种数据结构,用于存储一系列无序的键值对
映射的零值为 nil
。nil
映射既没有键,也不能添加键。
创建并初始化映射
1、使用内置的make
函数
2、使用映射字面量
//1、使用make声明映射,创建一个映射,键的类型是string,值的类型是int
d := make(map[string]int)
d["a"] = 1
fmt.Println(d["a"]) //1
//2、为映射赋值时,{}很重要,没有的话相当于对nil赋值会发生运行错误
b := map[string]int{}
b["s"] = 11
fmt.Println(b["s"])
//使用两个键值对初始化映射
c := map[string]int{"z": 9, "y": 10}
fmt.Println(c) //map[z:9 y:10]
//从映射取值有两个选择:
//1.同时获得值,表示键是否存在的标志
value, exists := c["y"]
if exists {
fmt.Println(value)
}
//2.只返回键的值
映射文法与结构体相似,不过必须有键名
若顶级类型只是一个类型名,你可以在文法的元素中省略它
type Vertex struct {
Lat, Long float64
}
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
func main() {
fmt.Println(m)
}
修改映射
m[key] = elem //在映射 m 中插入或修改元素
elem = m[key] //获取元素
delete(m, key) //删除元素
elem, ok := m[key] //通过双赋值检测某个键是否存在
//若key在m中,ok为true;否则,ok为false。
//若key不在映射中,那么elem是该映射元素类型的零值
函数值
函数也是值。它们可以像其它值一样传递。
函数值可以用作函数的参数或返回值。
import (
"fmt"
"math"
)
func compute(fn func(float64, float64) float64) float64 {
return fn(3, 4)
}
func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(5, 12)) //13
fmt.Println(compute(hypot)) //5
fmt.Println(compute(math.Pow)) //3**4=81
}
func f4(fn func(float64, float64) (float64, float64)) (float64, float64) {
return fn(4, 2)
}
func f3(fn, fm func(int, int) int) int {
return fn(4, 5) + fm(1, 2)
}
func f2(a, b float64) (float64, float64) {
return a + b, math.Pow(a, b)
}
func main() {
f1 := func(x, y int) int {
return x + y
}
fmt.Println(f1(1, 2)) //x + y=3
fmt.Println(f2(3, 2)) //a + b, math.Pow(a, b)=5 9
fmt.Println(f3(f1, f1)) //fn(4, 5) + fm(1, 2)=12
fmt.Println(f4(f2)) //6 16
}
函数的闭包(?)
Go 函数可以是一个闭包。
闭包的体现形式,就是用函数返回另一个函数
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos:= adder()
for i := 0; i < 10; i++ {
fmt.Println(pos(i))
}
}
运行结果
0 //sum=0+0
1 //sum=0+1=1
3 //sum=1+2=3
6 //sum=3+3=6
10 //sum=6+6=10
15
21
28
36
45
扩展阅读
附录
math包
fmt.Println(math.Abs(float64(i))) //绝对值
fmt.Println(math.Ceil(5.0)) //向上取整
fmt.Println(math.Floor(5.8)) //向下取整
fmt.Println(math.Mod(11, 3)) //取余数,同11%3
fmt.Println(math.Modf(5.26)) //取整数,取小数
fmt.Println(math.Pow(3, 2)) //x的y次方
fmt.Println(math.Pow10(4)) // 10的n次方
fmt.Println(math.Sqrt(8)) //开平方
fmt.Println(math.Cbrt(8)) //开立方
fmt.Println(math.Pi)
Go的基础内容差不多就是这些,剩下方法和接口、并发相关的知识,要开始深入了。