title: Golang
top: 31
date: 2020-11-13 16:55:31
tags:
- Golang基础
categories: Golang
变量声明
goLang
中用 var
声明变量,在goLang
中声明的变量必须得使用,否则编译不会通过
var name string
var age int
var flag bool
// 多个变量声明
var (
var name string
var age int
var flag bool
)
// 声明变量并赋值
var name string = "张三"
常量与iota
const pi = 3.14
const e = 2.718
const(
const pi = 3.14
const e = 2.718
)
const
同时声明多个常量时,如果省略了值,则表示和上面一行的值相同
const(
n1 = 100
n2
n3
)
// n1,n2,n3的值都是100
iota
iota
是goLang
的常量计数器,只能在常量表达式中使用。
iota
在const
关键字出现是将被重置为0。const
中每新增一行常量声明将使 iota
计数一次(iota
可理解为const
语句块中的行索引)
package main
import (
"fmt"
)
const (
n1 = iota
n2
n3
)
func main() {
fmt.Println(n1)
fmt.Println(n2)
fmt.Println(n3)
}
// print 0 ; 1 ; 2
// example 2
const (
n1 = iota
n2
_
n3
)
// _(短下划线表匿名变量)
// print 0 ; 1 ; 3
// example 3
const (
n1 = iota
n2 = 100
n3
n4 = iota
)
// print 0 ; 100 ; 100 ; 3
// example 4 多个iota定义在一行
const(
n1,n2 = iota+1, iota+2 // 1,2
n3,n4 // 2,3
n5,n6 // 3,4
)
定义数量级
const(
_ = iota
KB = 1<<(10*iota) // 1左移10位 1024
MB = 1<<(10*iota) // 1024 再左移10位
GB = 1<<(10*iota)
TB = 1<<(10*iota)
PB = 1<<(10*iota)
)
流程控制
if else(分支结构)
if 表达式1 {
分支1
} else if 表达式2 {
分支2
} else{
分支3
}
// example
func ifDemo1() {
if score := 65;score >= 90 {
fmt.Println("A")
} else if score > 75 {
fmt.Println("B")
} else {
fmt.Println("C")
}
}
for(循环结构)
for 初始语句;条件表达式;结束语句{
循环体语句
}
func forDemo() {
for i := 0; i < 10; i++ {
fmt.Println(i)
}
}
func forDemo2() {
i := 0
for ; i < 10; i++ { //省略初始
fmt.Println(i)
}
}
func forDemo3() {
i := 0
for i < 10 { // 省略初始与结束
fmt.Println(i)
i++
}
}
for循环可以通过break
、goto
、return
、panic
语句强制退出循环。
for range 键值循环
// 1 遍历数组/切片
for key, value := range []int{1, 2, 3, 4} {
fmt.Printf("key:%d value:%d\n", key, value)
}
// 2 遍历字符串
var str = "hello 你好"
for key, value := range str {
fmt.Printf("key:%d value:0x%x\n", key, value)
}
// 3 遍历map
m := map[string]int{
"hello": 100,
"world": 200,
}
for key, value := range m {
fmt.Println(key, value)
}
// 4 c := make(chan int)
go func() {
c <- 1
c <- 2
c <- 3
close(c)
}()
for v := range c {
fmt.Println(v)
}
switch case
func switchDemo1() {
finger := 3
switch finger {
case 1:
fmt.Println("大拇指")
case 2:
fmt.Println("食指")
case 3:
fmt.Println("中指")
case 4:
fmt.Println("无名指")
case 5:
fmt.Println("小拇指")
default:
fmt.Println("无效的输入!")
}
}
// 一个分支可以有多个值,多个case值中间使用英文逗号分隔。
func testSwitch3() {
switch n := 7; n {
case 1, 3, 5, 7, 9:
fmt.Println("奇数")
case 2, 4, 6, 8:
fmt.Println("偶数")
default:
fmt.Println(n)
}
}
// 分支还可以使用表达式,这时候switch语句后面不需要再跟判断变量。
func switchDemo4() {
age := 30
switch {
case age < 25:
fmt.Println("好好学习吧")
case age > 25 && age < 35:
fmt.Println("好好工作吧")
case age > 60:
fmt.Println("好好享受吧")
default:
fmt.Println("活着真好")
}
}
fallthrough
语法可以执行满足条件的case
的下一个case
func switchDemo5() {
s := "a"
switch {
case s == "a":
fmt.Println("a")
fallthrough
case s == "b":
fmt.Println("b")
case s == "c":
fmt.Println("c")
default:
fmt.Println("...")
}
}
/*output
a
b
*/
goto
(跳转到指定标签)
goto
语句通过标签进行代码间的无条件跳转。goto
语句可以在快速跳出循环、避免重复退出上有一定的帮助。
func gotoDemo2() {
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 {
// 设置退出标签
goto breakTag
}
fmt.Printf("%v-%v\n", i, j)
}
}
return
// 标签
breakTag:
fmt.Println("结束for循环")
}
数组
数组的长度是数组类型的一部分
// 定义一个长度为3元素类型为int的数组a
// var 数组变量名 [元素数量]T
var a [3]int
数组的初始化
func main() {
var testArray [3]int //数组会初始化为int类型的0值
var numArray = [3]int{1, 2} //使用指定的初始值完成初始化
var cityArray = [3]string{"北京", "上海", "深圳"} //使用指定的初始值完成初始化
fmt.Println(testArray) //[0 0 0]
fmt.Println(numArray) //[1 2 0]
fmt.Println(cityArray) //[北京 上海 深圳]
}
// 根据初始值的个数自行推断数组的长度
func main() {
var testArray [3]int
var numArray = [...]int{1, 2}
var cityArray = [...]string{"北京", "上海", "深圳"}
fmt.Println(testArray) //[0 0 0]
fmt.Println(numArray) //[1 2]
fmt.Printf("type of numArray:%T\n", numArray) //type of numArray:[2]int
fmt.Println(cityArray) //[北京 上海 深圳]
fmt.Printf("type of cityArray:%T\n", cityArray) //type of cityArray:[3]string
}
// 指定索引值的方式来初始化数组
func main() {
a := [...]int{1: 1, 3: 5}
fmt.Println(a) // [0 1 0 5]
fmt.Printf("type of a:%T\n", a) //type of a:[4]int
}
数组的遍历
func main() {
var a = [...]string{"北京", "上海", "深圳"}
// 方法1:for循环遍历
for i := 0; i < len(a); i++ {
fmt.Println(a[i])
}
// 方法2:for range遍历
for index, value := range a {
fmt.Println(index, value)
}
}
多维数组
二维数组的定义
func main() {
a := [3][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
fmt.Println(a) //[[北京 上海] [广州 深圳] [成都 重庆]]
fmt.Println(a[2][1]) //支持索引取值:重庆
}
多维数组只有第一层可以使用...
来让编译器推导数组长度
//支持的写法
a := [...][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
// 不支持 多维数组的内层使用...
b := [3][...]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
二维数组的遍历
func main() {
a := [3][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
for _, v1 := range a {
for _, v2 := range v1 {
fmt.Printf("%s\t", v2)
}
fmt.Println()
}
}
切片
// 切片类型声明
var 变量名 []类型
func main() {
// 声明切片类型
var a []string //声明一个字符串切片
var b = []int{} //声明一个整型切片并初始化
var c = []bool{false, true} //声明一个布尔切片并初始化
var d = []bool{false, true} //声明一个布尔切片并初始化
fmt.Println(a) //[]
fmt.Println(b) //[]
fmt.Println(c) //[false true]
fmt.Println(a == nil) //true
fmt.Println(b == nil) //false
fmt.Println(c == nil) //false
// fmt.Println(c == d) //切片是引用类型,不支持直接比较,只能和nil比较
}
切片拥有自己的长度和容量,我们可以通过使用内置的len()
函数求长度,使用内置的cap()
函数求切片的容量。
切片表达式
a[low : high : max]
切片表达式中的low
和high
表示一个索引范围(左包含,右不包含),容量为max-low
。
func main() {
a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5]
fmt.Printf("t:%v len(t):%v cap(t):%v\n", t, len(t), cap(t))
}
// output t:[2 3] len(t):2 cap(t):4
使用make()函数构造切片
make([]T, size, cap)
/*
T:切片的元素类型
size:切片中元素的数量
cap:切片的容量
*/
func main() {
a := make([]int, 2, 10)
fmt.Println(a) //[0 0]
fmt.Println(len(a)) //2
fmt.Println(cap(a)) //10
}
判断切片是否为空
要检查切片是否为空,请始终使用len(s) == 0
来判断,而不应该使用s == nil
来判断。
切片的赋值拷贝
func main() {
s1 := make([]int, 3) //[0 0 0]
s2 := s1 //将s1直接赋值给s2,s1和s2共用一个底层数组
s2[0] = 100
fmt.Println(s1) //[100 0 0]
fmt.Println(s2) //[100 0 0]
}
append()方法为切片添加元素
func main(){
var s []int
s = append(s, 1) // [1]
s = append(s, 2, 3, 4) // [1 2 3 4]
s2 := []int{5, 6, 7}
s = append(s, s2...) // [1 2 3 4 5 6 7] ...表示拆开
}
var citySlice []string
// 追加一个元素
citySlice = append(citySlice, "北京")
// 追加多个元素
citySlice = append(citySlice, "上海", "广州", "深圳")
// 追加切片
a := []string{"成都", "重庆"}
citySlice = append(citySlice, a...)
fmt.Println(citySlice) //[北京 上海 广州 深圳 成都 重庆]
从切片中删除元素
Go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。
func main() {
// 从切片中删除元素
a := []int{30, 31, 32, 33, 34, 35, 36, 37}
// 要删除索引为2的元素
a = append(a[:2], a[3:]...)
fmt.Println(a) //[30 31 33 34 35 36 37]
}
使用copy()函数复制切片
由于切片是引用类型,所以a和b其实都指向了同一块内存地址。修改b的同时a的值也会发生变化。
Go语言内建的copy()
函数可以迅速地将一个切片的数据复制到另外一个切片空间中,类似于python中的深复制。
func main() {
// copy()复制切片
a := []int{1, 2, 3, 4, 5}
c := make([]int, 5, 5)
copy(c, a) //使用copy()函数将切片a中的元素复制到切片c
fmt.Println(a) //[1 2 3 4 5]
fmt.Println(c) //[1 2 3 4 5]
c[0] = 1000
fmt.Println(a) //[1 2 3 4 5]
fmt.Println(c) //[1000 2 3 4 5]
}
Go语言中的指针
取变量指针的语法如下:
ptr := &v // v的类型为T
/*
v:代表被取地址的变量,类型为T
ptr:用于接收地址的变量,ptr的类型就为*T,称做T的指针类型。*代表指针。
*/
func main() {
a := 10
b := &a
fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078
fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*int
fmt.Println(&b) // 0xc00000e018
}
指针取值
func main() {
//指针取值
a := 10
b := &a // 取变量a的地址,将指针保存到b中
fmt.Printf("type of b:%T\n", b)
c := *b // 指针取值(根据指针去内存取值)
fmt.Printf("type of c:%T\n", c)
fmt.Printf("value of c:%v\n", c)
}
总结: 取地址操作符&
和取值操作符*
是一对互补操作符,&
取出地址,*
根据地址取出地址指向的值。
new()与make()
var a1 *int // 未赋值的初始化时,指向空指针
var a2 = new(int) // 创建指针
fmt.println(a1) // <nil>
fmt.println(a2) // 0xc000540a8
make也是用于内存分配的,区别于new,它只用于slice、map、chan
的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型。
func main() {
var b map[string]int
b = make(map[string]int, 10)
b["沙河娜扎"] = 100
fmt.Println(b)
}
new与make的区别
- 二者都是用来做内存分配的。
- make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;
- 而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。
map
Go语言中提供的映射关系容器为map
,其内部使用散列表(hash)
实现。
map是一种无序的基于key-value
的数据结构,Go语言中的map是引用类型,必须初始化才能使用。用make()初始化。
//定义语法如下
map[KeyType]ValueType
/*
KeyType:表示键的类型
ValueType:表示键对应的值的类型
*/
make(map[KeyType]ValueType, [cap]) //cap表示map的容量
map基本使用
func main() {
scoreMap := make(map[string]int, 8)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
fmt.Println(scoreMap)
fmt.Println(scoreMap["小明"])
fmt.Printf("type of a:%T\n", scoreMap)
}
/* output
map[小明:100 张三:90]
100
type of a:map[string]int
*/
func main() {
userInfo := map[string]string{
"username": "沙河小王子",
"password": "123456",
}
fmt.Println(userInfo) //
}
判断某个键是否存在
value, ok := map[key]
func main() {
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
// 如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值
v, ok := scoreMap["张三"]
if ok {
fmt.Println(v)
} else {
fmt.Println("查无此人")
}
}
map的遍历
func main() {
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
for k, v := range scoreMap {
fmt.Println(k, v)
}
}
delete()函数删除键值对
delete(map, key)
func main(){
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
delete(scoreMap, "小明")//将小明:100从map中删除
for k,v := range scoreMap{
fmt.Println(k, v)
}
}
按指定顺序遍历map
func main() {
rand.Seed(time.Now().UnixNano()) //初始化随机数种子
var scoreMap = make(map[string]int, 200)
for i := 0; i < 100; i++ {
key := fmt.Sprintf("stu%02d", i) //生成stu开头的字符串
value := rand.Intn(100) //生成0~99的随机整数
scoreMap[key] = value
}
//取出map中的所有key存入切片keys
var keys = make([]string, 0, 200)
for key := range scoreMap {
keys = append(keys, key)
}
//对切片进行排序
sort.Strings(keys)
//按照排序后的key遍历map
for _, key := range keys {
fmt.Println(key, scoreMap[key])
}
}
函数
函数定义
func 函数名(参数)(返回值){
函数体
}
- 函数名:由字母、数字、下划线组成。但函数名的第一个字母不能是数字。在同一个包内,函数名也称不能重名(包的概念详见后文)。
- 参数:参数由参数变量和参数变量的类型组成,多个参数之间使用
,
分隔。 - 返回值:返回值由返回值变量和其变量类型组成,也可以只写返回值的类型,多个返回值必须用
()
包裹,并用,
分隔。 - 函数体:实现指定功能的代码块。
func intSum(x int, y int) int {
return x + y
}
// 参数的类型简写。多个连续的参数类型一致时,可以只在最后统一声明。
func intSum(x, y int) int {
return x, y
}
// 可变长参数,y可以传入n(n>=0)个数,相当于切片
func intSum(x, y ...int) int {
return x, y
}
/*
返回值可以命名也可以不命名;
命名的返回值相当于在函数中声明一个变量
*/
func intSum(x int, y int) (ret int) {
ret = x + y
return // 使用命名的返回值return后可以省略
}