# Go入门
配置proxy goproxy.cn
# 资料
Go标准库文档-Go语言中文网 (opens new window)
# go的命令行
运行go语言:
go run hello.go
生成二进制文件:
go build hello.go
可以直接运行build生成的二进制文件:
$ ./hello
# 基础语法
# 变量定义
# 函数返回
函数返回值处可以直接定义返回值的变量名,在函数体内直接使用此变量名;并且return时,可以不用显式return哪个变量。
example:
func example(num int) (rtn int) {
rtn += 1
return
}
# 条件判断
# 标记public、protect
以标识符的大小写区分“对象”的可见性
# 数据类型
# string类型
定义字符串
var str string
str := "我是字符串"
var str = "i am string"
通过下标获取某一位字符
import "fmt"
fmt.Println(str[3])
// 但是不允许修改,此处是错误示例
str[1] = "a"
获取字符串的长度(要注意编码问题),因为utf-8编码中,一个汉字需要3个字节编码,len()输出的是字符串占据的字节数。需要打印字符长度,可以使用rune。
fmt.Println(len("hello")) // 结果是5
fmt.Println(len("hello世界")) // 结果是11
将string转化为rune数组,再计算长度
str := "hello世界"
fmt.Println(len([]rune(str))) // 结果是7
循环字符串
for _, ch := range str {
fmt.Println(int(ch-'0'))
}
# 类型转换
# string和int互相转换
- string转成int: int, err := strconv.Atoi(string)
- string转成int64: int64, err := strconv.ParseInt(string, 10, 64)
- int转成string: string := strconv.Itoa(int)
- int64转成string: string := strconv.FormatInt(int64,10)
# 指针
指针变量指向一个值的内存地址。
正常定义的变量是有存储在一个地址上的,可以通过&获取正常变量的地址赋值给指针变量:
// 定义正常变量
var a int = 20
// 指向int类型变量的地址
var p *int
// 将正常变量的地址赋值给指针变量
p = &a
fmt.Println(&a) // 结果是a变量的地址
fmt.Println(p) // 结果同上
// 使用指针访问对应地址上的值
fmt.Println(*ip) // 结果为 20
声明指针
// 基础类型
var variableName *int
// Book可以是结构体
var variableName *Book
Go的空指针
当指针被定义后,没有被分配到任何变量,则指针的值为
nil。
向函数传递指针
通过传递指针,可以在调用函数中修改对应地址的值。
func main() {
var a int = 100
var b int = 200
swap(&a, &b)
fmt.Println(a) // result: 200
fmt.Println(b) // result: 100
}
func swap(a, b *int) {
*a, *b = *b, *a
}
指向指针的指针
一个指针变量存放的是另一个指针变量的地址,则这个指针被称为
指向指针的指针变量
// 定义
var ptr **int
示例:
import "fmt"
func main() {
var a int
var ptr *int
var pptr **int
a = 100
ptr = &a
pptr = &ptr
fmt.Println(a) // 三个print结果相同,都是 100
fmt.Println(*ptr)
fmt.Println(**pptr)
}
指针数组
func main() {
const n = 3
arr := []int{100, 200, 300}
var ptr [n]*int
for i := range arr {
ptr[i] = &arr[i]
}
for _,val := range ptr {
fmt.Println(*val) // 打印arr中的结果
}
}
# 结构体
定义结构体
type theStruct struct {
member definition
member definition
member definition
}
变量声明
variableName := theStruct {value1, value2, value3}
variableName := theStruct {key1 : value1, key2 : value2, key3: value3}
访问结构体成员
使用结构体.属性
import "fmt"
type Book struct {
title string
author string
id int
}
func main {
var b1 Book
var b2 Book
b1.title = "深入理解Java虚拟机"
b1.author = "周志明"
bi.id = 1001
b2.title = "my book"
b2.author = "jesse"
b2.id = 1002
fmt.Println(b1.title)
fmt.Println(b1.author)
fmt.Println(b1.id)
fmt.Println(b2.title)
fmt.Println(b2.author)
fmt.Println(b2.id)
}
结构体作为函数参数
func main() {
var b1 Book
b1.title = "my book"
printBook(b1)
}
func printBook(b Book) {
fmt.Println(b.title)
}
结构体指针
// 定义结构体指针
var b1 *Book
// 查看结构体变量地址
var addr = &b1
// 使用结构体指针访问结构体成员
b1.title
结构体指针示例
func main() {
var b1 Book
b1.title = "my book"
b1.author = "jesse"
printBook(&b1)
}
func printBook(b *Book) {
fmt.Println(b.title)
fmt.Println(b.author)
}
给结构体定义方法
func main() {
// m是指针,此处是不是指针都不影响a,b的结果
m := &Mutatable{0, 1}
fmt.Println(m) // result: &{0 1}
m.StayTheSame()
fmt.Println(m) // result: &{0 1}
m.Mutate()
fmt.Println(m) // result: &{5 7}
}
type Mutatable struct {
a int
b int
}
func (m Mutatable) StayTheSame() {
// 不是指针对象,无法修改外部的值,函数内的修改只在函数内生效
m.a = 5
m.b = 7
}
func (m *Mutatable) Mutate() {
// 指针修改可以影响函数外的值
m.a = 5
m.b = 7
}
# 数组和切片slice
# 数组
数组是由固定长度和固定对象类型组成的。
数组声明时,要方括号内要写明数组长度或者使用...符号自动计算长度:
// 初始化为0
var a [4]int
// 输出结果为[1 2 3]
var a [3]int = [3]int{1,2,3}
// 输出结果为[1 2 0]
var a [3]int = [3]int{1,2}
// 使用...符号,代替长度定义,自动根据初始化值的数量计算长度
var a = [...]int{1,2,3}
// 定义简写
a := [3]int{1,2,3}
go中的数组是一个实体对象,而不是指针。
并且数组的长度是数组的组成部分,长度一定是常量表达式,必须在编译阶段确定。
如果先定义一个变量为[3]int,则无法再将[4]int赋值给它了:
a := [3]int{1,2,3}
a = [4]int{1,2,3,4} // 编译报错,无法赋值
比较数组是否相等?
两个数组的长度和元素类型相同的话,可以直接使用等号或不等号比较。
只有当两个数组的所有元素都相等时,数组才算相等。
遍历数组
arr := [3]string{"a", "b", "c"}
for k, v := range arr {
fmt.Println(k, v)
}
// 输出结果为
// 0 a
// 1 b
// 2 c
# 切片
切片是一个引用类型,类似于Java中的数组类型。(注意终止索引不包含在内,与Java一致)
切片的内部结构包含地址(开始位置)、大小和容量。
声明一个切片
var a []int // 声明语法,此时切片还没有被分配内存
var b = []int{} // 声明一个空切片,此时已经分配了内存,只是还没有元素
fmt.Println(a == nil) // true,声明但未使用的切片默认值是nil
fmt.Println(b == nil) // false
从数组或切片可以生成新的切片:
var a = [3]int{1,2,3}
fmt.Println(a, a[1:2]) // 输出 [1 2 3] [2]
从上面代码可看出语法格式为:slice[开始位置:终止位置],但要记住,结果是不包含终止位置的。当任一位置缺失时,像slice[:1]或slice[5:],表示从开头或者到结尾。
使用make构造切片
make([]Type, size, cap) // 声明语法
a := make([]int, 2)
b := make([]int, 2, 10)
fmt.Println(a, b) // [0 0] [0 0]
fmt.Println(len(a), len(b)) // 2 2
Type代表切片元素类型;size是分配多少个元素;cap为预分配的元素数量,用于提前分配空间,降低多次分配空间造成的性能问题,不影响size。
使用make分配切片一定会发生内存分配操作,但使用slice[start:finish]只是将新的切片结构指向已经分配好的内存区域,不发生内存分配操作。
清空切片:
a := []int{1,2,3}
fmt.Println(a[:]) // 清空切片
用append为切片追加元素
var a []int
a = append(a, 1) // 追加1个元素
a = append(a, 1, 2, 3) // 追加多个元素, 手写解包方式
a = append(a, []int{1,2,3}...) // 追加一个切片, 切片需要解包
追加切片时,若空间不足,切片就会进行扩容,长度会发生变化。切片扩容的规律是按照2倍数进行扩容:
var numbers []int
for i := 0; i < 10; i++ {
numbers = append(numbers, i)
fmt.Printf("len: %d cap: %d pointer: %p\n", len(numbers), cap(numbers), numbers)
}
数组和切片互转
通过[0:2]的写法将数组转为切片
arr := [3]int{1,2,3}
fmt.Println(arr[0:2]) // 输出 [1 2]
fmt.Println((arr[0:2])[0:1]) // 输出 [1]
将切片的内容复制到数组
slice := []int{1, 2, 3}
var arr [3]int
fmt.Println(copy(arr[0:2], slice)) // 输出 2,代表将切片中
fmt.Println(arr[0:2])
其他
获取数组长度
使用自带函数len()
go中的数组长度只能指定常量值,若需要动态指定,则需要使用切片
slice := make([]int, length)
# 其他数据结构
除了基本数据结构外,整理了一些常用的数据结构
# Map集合
定义map
// 创建集合
var theMap map[string]string
var theMap = make(map[string]string)
theMap := map[string]string{"a": "1", "b": "2", "c": "3"}
向map插入值
theMap["key"] = "value"
循环输出
for k := range theMap {
fmt.Println(k, theMap[k])
}
查看元素在集合中是否存在
val, ok := theMap[key]
if (ok) {
// 存在
} else {
// 不存在
}
删除集合元素
delete(theMap, key)
// like this
delete(theMap, "a")
# fmt包
格式化IO函数
fmt.Println("")
fmt.Sprintf(patter, s1, s2...);
格式化字符串:%d 表示整型数字,%s 表示字符串
fmt.Printf("%v %v %v %q\n", i, f, b, s)
# strings包
Trim/TrimLeft/TrimRight...
用于修剪字符串,返回字符串的slice
Join
将数组join起来,中间添加内容
strings.Join(arrays, ",")
Fields
将字符串按空格分隔
strings.Fields(str)
# sort包
数组排序
import "sort"
func example(nums []int) []int {
sort.Ints(nums)
return nums
}
排序数组中部分内容
sort.Ints(nums[i:j])
# math包
i := -100
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)
# 自实现的工具类
# PowInt-求int指数结果
import "math"
func powInt(x, y int) int {
return int(math.Pow(float64(x), float64(y)))
}
# 翻转字符串
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
# 数组中交换两个值
func swap(nums []int, a, b int) {
nums[a], nums[b] = nums[b], nums[a]
}
# 字符串排序
import "sort"
type sortRunes []rune
func (s sortRunes) Less(i, j int) bool {
return s[i] < s[j]
}
func (s sortRunes) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s sortRunes) Len() int {
return len(s)
}
func SortString(s string) string {
r := []rune(s)
sort.Sort(sortRunes(r))
return string(r)
}
func main() {
w1 := "bcad"
w2 := SortString(w1) // abcd
}
Summary →