01_01_Go语言基础知识
- 定义变量
- 内建变量类型
- 强制类型转换
- 常量与枚举
- 要点总结
- 条件语句
- if 举例
- switch 举例
- for 循环
- 函数
- 指针
- 数组
定义变量
使用 var 关键字
使用 var
关键字, 可以放在函数内, 或直接放在包内均可
// 定义 a, b, c 三个变量 类型都是 bool 变量可以不赋初始值
var a, b, c bool
// 定义 s1, s2 两个变量 类型都是 string 定义变量的同时赋初始值
var s1, s2 string = "hello", "world"
// 使用 var 集中定义变量
var (
age = 18
name = "baoxing"
isMan = true
)
// 让编译器自动决定类型, 并且使用 := 定义变量 注意 := 只能在函数内使用
a, b, c, s := 3, 4, true, "def"
内建变量类型
-
bool
,string
-
(u)int
,(u)int8
,(u)int16
,(u)int32
,(u)int64
,uintptr
-
byte
,rune
-
float32
,float64
,complex64
,complex128
强制类型转换
类型转换是强制的
var a, b int = 3, 4
// 错误 编译不通过 因 Sqrt 参数类型 为 float64 返回类型 为 float64
var c = math.Sqrt(a * a + b * b)
// 正确 强制转换类型后
var c = int(math.Sqrt(float64(a*a + b*b)))
常量与枚举
- 使用
const
关键字, 可以指定类型 也可以不指定类型(不指定时可以作为各种类型使用,如下面的 4 中示例 )
const a, b = 3, 4
const a, b int = 3, 4
- 可以定义在函数内, 也可以定义在包内
// 1) 定义在函数内
func consts() {
const filename = "abc.txt"
fmt.Println(filename)
}
// 2) 定义在包内
const filename = "abc.txt"
func consts() {
fmt.Println(filename)
}
- 可以一组常量集中定义
func consts() {
const (
filename = "abc.txt"
a, b = 3, 4
)
fmt.Println(filename, a, b)
}
- 定义常量时不指定类型, 此时因为
math.Sqrt()
需要的参数为float64
此时 常量类型就是float64
func consts() {
const a, b = 3, 4
var c = int(math.Sqrt(a*a + b*b))
fmt.Println( c)
}
- 枚举类型(特殊的常量)
- 普通枚举类型
- 自增枚举类型
// 常规用法
func enums() {
const (
cpp = 0
java = 1
python = 2
golang = 3
)
fmt.Println(cpp, java, python, golang)
// 打印输出 0 1 2 3
}
// ==========================================================================
// 简化写法 自增值枚举类型
func enums() {
const (
cpp = iota
java
python
golang
)
fmt.Println(cpp, java, python, golang)
// 打印输出 0 1 2 3
}
// ==========================================================================
// 跳过 中间的 1
func enums() {
const (
cpp = iota
_
python
golang
javascript
)
fmt.Println(cpp, python, golang, javascript)
// 打印输出 0 2 3 4
}
// ==========================================================================
// 高级用法
func enums() {
const (
b = 1 << (10 * iota)
kb
mb
gb
tb
pb
)
fmt.Println(b, kb, mb, gb, tb, pb)
// 打印输出 1 1024 1048576 1073741824 1099511627776 1125899906842624
}
要点总结
- 变量类型写在变量名之后
- 编译器可以推测变量类型
- 没有
char
只有rune
- 原生支持复数类型
条件语句
if
的条件里不需要括号if
的条件里可以赋值switch
会自动break
, 除非使用fallthrough
switch
后面可以没有表达式, 可以直接switch
多个条件
if 举例
// 常规写法
func main() {
const filename = "abc.txt"
contents, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s \n", contents)
}
}
// ==========================================================================
// 特有写法 先执行前面的赋值 再判断 注意此时的 contents 仅在 if 或 else 内部可以访问到
func main() {
const filename = "abc.txt"
if contents, err := ioutil.ReadFile(filename); err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s \n", contents)
}
}
switch 举例
func eval(a, b int, op string) int {
var result int
switch op {
case "+":
result = a + b
case
"-":
result = a - b
case
"*":
result = a * b
case "/":
result = a / b
default:
panic("unsupported operator:" + op)
}
return result
}
func main() {
fmt.Println(eval(1, 2, "+"))
// 输出打印 3
}
// ==========================================================================
// switch 后面没有表达式写法
func grade(score int) string {
g := ""
switch {
case score < 0 || score > 100:
panic(fmt.Sprintf("Wrong score: %d", score))
case score < 60:
g = "F"
case score < 80:
g = "C"
case score < 90:
g = "B"
case score < 100:
g = "A"
}
return g
}
func main() {
fmt.Println(
grade(0),
grade(59),
grade(60),
grade(82),
grade(99),
grade(100),
//grade(101),
)
// 输出打印 F F C B A
}
for 循环
for
的条件里不需要括号for
的条件里可以省略初始条件, 结束条件, 递增表达式- 没有
while
// 基本使用
func basic() int {
sum := 0
for i := 0; i <= 100; i++ {
sum += i
}
return sum
}
func main() {
fmt.Println(basic())
}
// ==========================================================================
// 省略初始条件
func convertToBin(n int) string {
result := ""
for ; n > 0; n /= 2 {
lsb := n % 2
result = strconv.Itoa(lsb) + result
}
return result
}
func main() {
fmt.Println(
convertToBin(5), // 打印 101
convertToBin(13), // 打印 1101
)
}
// ==========================================================================
// 只有结束条件
func printFile(filename string) {
file, err := os.Open(filename)
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(file)
// 只有结束条件
for scanner.Scan() {
// 一次读取一行
fmt.Println(scanner.Text())
}
}
func main() {
printFile("abc.txt")
}
// ==========================================================================
// 省略 初始条件, 结束条件, 递增表达式 (无限循环)
func forever() {
for {
fmt.Println("abc")
}
}
func main() {
forever()
}
函数
- 返回值类型写在最后面
- 可以返回多个值
- 函数可以作为参数
- 函数返回多个值时可以起名字(仅用于非常简单的函数, 对于调用者而言没有区别)
- 可变参数列表
- 没有默认参数, 可选参数
// 基本使用
func eval(a, b int, op string) int {
switch op {
case "+":
return a + b
case "-":
return a - b
case "*":
return a * b
case "/":
return a / b
default:
panic("unsupported operation: " + op)
}
}
func main() {
fmt.Println(eval(3, 4, "*")) // 打印输出 12
}
// ==========================================================================
// 多个返回值
// 13 / 3 = 4 ... 1
func div(a, b int) (int, int) {
return a / b, a % b
}
func main() {
fmt.Println(div(13, 3)) // 打印输出 4 1
}
// ==========================================================================
// 多个返回值带名称
// 13 / 3 = 4 ... 1 q 代表商 r 代表余数
func div(a, b int) (q, r int) {
//q = a / b
//r = a % b
//return
return a / b, a % b
}
func main() {
q, r := div(13, 3)
fmt.Println(q, r) // 打印输出 4 1
}
// ==========================================================================
// 多个返回值 匿名变量的使用 _
// q 代表商 r 代表余数
func div(a, b int) (q, r int) {
return a / b, a % b
}
func eval(a, b int, op string) int {
switch op {
case "+":
return a + b
case "-":
return a - b
case "*":
return a * b
case "/":
q, _ := div(a, b)
return q
default:
panic("unsupported operation: " + op)
}
}
func main() {
fmt.Println(eval(28, 4, "/")) // 打印输出 7
}
// ==========================================================================
// 多个返回值 Go 语言常用方式
func eval(a, b int, op string) (res int, error error) {
switch op {
case "+":
return a + b, nil
case "-":
return a - b, nil
case "*":
return a * b, nil
case "/":
return a / b, nil
default:
return 0, fmt.Errorf("unsupported operation: %s", op)
}
}
func main() {
fmt.Println(eval(28, 4, "**")) // 打印输出: 0 unsupported operation: **
if result, err := eval(3, 4, "**"); err != nil {
fmt.Println("Error:", err) // 打印输出: Error: unsupported operation: **
} else {
fmt.Println(result)
}
}
// ==========================================================================
// 函数式编程思想 重写 eval 函数 注意: 形参为函数
// 写法 1 单独定义个函数 pow 传递给 apply 函数使用
// apply 函数有三个参数 分别为 op, a , b
func apply(op func(int, int) int, a, b int) int {
pointer := reflect.ValueOf(op).Pointer()
opName := runtime.FuncForPC(pointer).Name()
fmt.Printf("Calling function %s with args (%d. %d) \n", opName, a, b)
// 将 a, b 参数传递给 op 函数使用
return op(a, b)
}
func pow(a, b int) int {
return int(math.Pow(float64(a), float64(b)))
}
func main() {
res := apply(pow, 3, 4)
fmt.Println(res) // 打印输出
}
// 写法 2 给 apply 函数传递一个匿名函数
// apply 函数有三个参数 分别为 op, a , b
func apply(op func(int, int) int, a, b int) int {
pointer := reflect.ValueOf(op).Pointer()
opName := runtime.FuncForPC(pointer).Name()
fmt.Printf("Calling function %s with args (%d. %d) \n", opName, a, b)
// 将 a, b 参数传递给 op 函数使用
return op(a, b)
}
func main() {
// 匿名函数写法
res := apply(
func(a int, b int) int {
return int(math.Pow(float64(a), float64(b)))
}, 3, 4)
fmt.Println(res) // 打印输出
}
// ==========================================================================
// 可变参数列表
func sum(numbers ...int) int {
s := 0
for i := range numbers {
s += numbers[i]
}
return s
}
func main() {
fmt.Println(sum(1, 2, 3)) // 打印输出 6
}
指针
go
语言的指针不能运算
func main() {
// a 是 int 等于 2
var a int = 2
// pa 是 a 的指针, &a 表示 a 的地址 go 语言 *int 代表的是指针
var pa *int = &a
*pa = 3
fmt.Println(a) // 打印输出 3
}
参数传递
Go 语言只有 值 传递一种方式
- 值传递(做了一份拷贝,拷贝完之后和原来的没有关系,不会改变原来的值)
- 引用传递(不拷贝) — 在 Go语言中不存在
以下为 C++
中的一段代码
Go
语言是使用指针和值传递来配合使用, 来解决拷贝效率问题
// 例1
func swap(a, b int) {
b, a = a, b
}
func main() {
a, b := 3, 4
swap(a, b)
fmt.Println(a, b) // 打印输出 3 4 因为 a, b 是值传递 传过去的只是拷贝的值
}
// 例2
func swap(a, b *int) {
*b, *a = *a, *b
}
func main() {
a, b := 3, 4
swap(&a, &b)
fmt.Println(a, b) // 打印输出 4 3 因为交换的是地址, a 所指向的内容给 b, b 所指向的内容给 a
}
// 例3
func swap(a, b int) (int, int) {
// 把交换的结果返回回去
return b, a
}
func main() {
a, b := 3, 4
// 用 a, b 去接收返回结果
a, b = swap(a, b)
fmt.Println(a, b) // 打印输出 4 3 这种 swap 定义方法才是更好的 相对于 例2
}
数组
- 数量写在类型前
- 可通过
_
来省略变量 - 不仅
range
, 任何地方都可以通过_
省略变量 - 如果只要
i
下标 , 可以写成for i := range numbers
// 例1
func main() {
// 定义数组 先定义长度再定义类型
var arr1 [5]int
// 使用 := 定义数组
arr2 := [3]int{1, 3, 5}
// 让编辑器计算长度
arr3 := [...]int{2, 4, 6, 8}
// 定义一个二维数组 4行5列 从前往后读
var grade [4][5]int
fmt.Println(arr1, arr2, arr3)
// 打印输出
// [0 0 0 0 0] [1 3 5] [2 4 6 8]
fmt.Println(grade)
// 打印输出
// [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]
}
// 例2
// 遍历数组 第一种方式
func main() {
arr3 := [...]int{2, 4, 6, 8}
// 遍历数组
for i := 0; i < len(arr3); i++ {
fmt.Println(arr3[i])
}
}
// 输出
//2
//4
//6
//8
// 遍历数组 第二种方式
func main() {
arr3 := [...]int{2, 4, 6, 8}
// 遍历数组
for i := range arr3 {
fmt.Println(arr3[i])
}
}
// 输出
//2
//4
//6
//8
// 遍历数组 获取下标同时获取值
func main() {
arr3 := [...]int{2, 4, 6, 8}
// 遍历数组
for i, v := range arr3 {
fmt.Println(i, v)
}
}
// 输出
//0 2
//1 4
//2 6
//3 8
// 遍历数组 只想获取值不想获取下标
func main() {
arr3 := [...]int{2, 4, 6, 8}
// 遍历数组
for _, v := range arr3 {
fmt.Println(v)
}
}
// 输出
//2
//4
//6
//8
为什么要用 range
- 意向明确, 美观
- C++ 没有类似能力 有其他第三方的库
Java/Python
只能for each value
, 不能同时获取i, v
- [3]int 和 [5]int 是不同类型
- 调用
func f(arr [10]int)
会 拷贝数组 - 在
go
语言中一般不直接使用数组
数组是值类型
// 例1
func printArr(arr [5]int) {
for i, v := range arr {
fmt.Println(i, v)
}
}
func main() {
var arr1 [5]int
//arr2 := [3]int{1, 3, 5}
arr3 := [...]int{2, 4, 6, 8, 10}
printArr(arr1)
//printArr(arr2) // 编译出错 [3]int 和 [5]int 是两种类型
fmt.Println("==================")
printArr(arr3)
}
// 打印输出
//0 0
//1 0
//2 0
//3 0
//4 0
//==================
//0 2
//1 4
//2 6
//3 8
//4 10
// 例2 可以说明数组是值类型
func printArr(arr [5]int) {
for i, v := range arr {
fmt.Println(i, v)
}
arr[0] = 100
}
func main() {
var arr1 [5]int
arr3 := [...]int{2, 4, 6, 8, 10}
printArr(arr1)
fmt.Println("==================")
printArr(arr3)
fmt.Println(arr1, arr3)
}
// 打印输出
//0 0
//1 0
//2 0
//3 0
//4 0
//==================
//0 2
//1 4
//2 6
//3 8
//4 10
//[0 0 0 0 0] [2 4 6 8 10]
// 例3 可以说明数组是值类型
func printArr(arr [5]int) {
arr[0] = 100
for i, v := range arr {
fmt.Println(i, v)
}
}
func main() {
var arr1 [5]int
arr3 := [...]int{2, 4, 6, 8, 10}
printArr(arr1)
fmt.Println("==================")
printArr(arr3)
fmt.Println(arr1, arr3)
}
// 打印输出
//0 100
//1 0
//2 0
//3 0
//4 0
//==================
//0 100
//1 4
//2 6
//3 8
//4 10
//[0 0 0 0 0] [2 4 6 8 10]
// 例4 使用指针传递,就可以改变数组的值了
func printArr(arr *[5]int) {
arr[0] = 100
for i, v := range arr {
fmt.Println(i, v)
}
}
func main() {
arr3 := [...]int{2, 4, 6, 8, 10}
printArr(&arr3)
fmt.Println(arr3)
}
// 打印输出
//0 100
//1 4
//2 6
//3 8
//4 10
//[100 4 6 8 10]