Go基础

变量和常量

关键字&保留字

# 25个保留字
    break        default      func         interface    select
    case         defer        go           map          struct
    chan         else         goto         package      switch
    const        fallthrough  if           range        type
    continue     for          import       return       var

# 37个保留字

Constants:    true  false  iota  nil

Types:  int  int8  int16  int32  int64  
        uint  uint8  uint16  uint32  uint64  uintptr
        float32  float64  complex128  complex64
        bool  byte  rune  string  error

Functions:  make  len  cap  new  append  copy  close  delete
            complex  real  imag
            panic  recover

注意

1. 函数外的每个语句都必须以关键字开始(var、const、func等)
2. :=不能使用在函数外。
3. _多用于占位,表示忽略值(匿名变量,不占用命名空间,不会分配内存,不需要存在重复申明)

变量

package main

import (
    "fmt"
)
// 全局变量m
var m = 100

func main() {
    n := 10
    m := 200 // 此处声明局部变量m
    fmt.Println(m, n)
}

常量

常量是恒定不变的值

const pi = 3.1415

const (
    // const声明多个常量时,如果省略了值则表示和上面一行的值相同
    p1 = 3.1415
    p2 // 3.1415
    p3 // 3.1415
)

iota

iota是go语言的常量计数器,只能在常量的表达式中使用。iota在const关键字出现时将被重置为0。const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)

const (
    n1 = iota // 0
    n2 // 1
    n3 // 2
    _   // 跳过某些值
    n4 // 3
    n5 = 10 // 10
    n6 // 4
)

const (
    _ = iota // 0
    KB = 1 << (10 * iota) // 1 1<<10 2^10 1024
    MB = 1 << (10 * iota) // 2 1<<20 2^20 1024*1024
)

const (
    a, b = iota + 1, iota + 2 // 1,2
    c, d  // 2, 3
)

基本数据类型

package main

import (
    "fmt"
    "math"
)

func main() {
    var a int = 17 // int 型 (int64)
    fmt.Printf("%d \n", a) // 17 十进制
    fmt.Printf("%b \n", a) // 10001 二进制
    fmt.Printf("%o \n", a) // 021 八进制
    fmt.Printf("%x \n", a) // 0x11 16禁止

    fmt.Printf("%.2f \n", math.Pi) // 默认都是浮点型float64

    var s = "666"
    fmt.Printf("%T \n", s) // 类型 string 
    fmt.Printf("%v \n", s) // 值 666
    fmt.Printf("%#v \n", s) // 值 "666"
}

注:

布尔类型变量的默认值为false。
Go 语言中不允许将整型强制转换为布尔型.
布尔型无法参与数值运算,也无法与其他类型进行转换

字符串

字符串需要使用"

s1 := "ysicing"

多行字符串需要使用```

s1 := `牛
牛 牛
牛
`

字符串操作

package main

import (
    "fmt"
    "strings"
)

func main() {
    a := "666"
    b := "777"
    c := a + "-" + b
    fmt.Println(len(a))                    // len 长度
    fmt.Println(fmt.Sprintf("%s%s", a, b)) // 拼接
    fmt.Println(a + b)                     // 拼接
    fmt.Print(strings.Split(c, "-")[0])    // 分割
    res := strings.Split(c, "-")           // 分割
    fmt.Println(res)

    fmt.Println(strings.HasPrefix(c, "6"), strings.HasSuffix(c, "6")) //前缀/后缀判断

    s := "China万岁"
    for i := 0; i < len(s); i++ { // byte
        fmt.Printf("%T %v(%c) \n", s[i], s[i], s[i])
    }
    fmt.Println()
    for _, j := range s { // rune
        fmt.Printf("%T %v(%c) \n", j, j, j)
    }
    fmt.Println()
}
  1. uint8类型, 或者叫 byte型 代表了ASCII码的一个字符.
  2. rune类型, 代表一个 UTF-8字符(中文), 实际是一个int32.
  3. 因为UTF8编码下一个中文汉字由3~4个字节组成,一个rune字符由一个或多个byte组成.

流程控制

if

if 表达式1 {
    分支1
} else if 表达式2 {
    分支2
} else{
    分支3
}

for

package main

import "fmt"

func main() {
    // 基本格式
    for i := 1; i <= 3; i++ {
        fmt.Println(i)
    }

    j := 1
    for ; j <= 3; j++ {
        fmt.Println(j)
    }

    k := 1
    for k <= 3 {
        fmt.Println(k)
        k++
    }

    s := "HellGo"
    for i, j := range s {
        fmt.Printf("%d--->%v(%T)-->%c\n", i, j, j, j) // rune
    }

    // 死循环
    //for {
    //    fmt.Println("666")
    //}
}

switch case

简化判断

a := 4
switch a {
    case a < 1:
        fmt.Println("1")
    case 2,3:
        fmt.Println("2")
    default:
        fmt.Println("3") // 3
} 

b := 3
switch a {
    case a < 1:
        fmt.Println("1")
    case 2,3:
        fmt.Println("2")
        fallthrough // 可以执行满足条件的case的下一个case, 了解即可
    default:
        fmt.Println("3") 
} 

// 23

goto

func main() {
    for i := 1; i <= 9999; i++ {
        for j := 3000; j <= i; j++ {
            if j < i {
                fmt.Printf("%d * %d = %d ", i, j, i*j)
            } else if j == i {
                fmt.Printf("%d * %d = %d\n", i, j, i*j)
                goto breakTag
            }
        }
    }

breakTag: // label
    fmt.Println("end")
}

运算符

算数运算符: + - * / % 关系运算符: == != > >= < << (结果为bool值) 逻辑运算符: && || ! (结果为bool值) 位运算符: & (相与 同为1为1)|(相或 有1为1) ^(相异 或不同为1) << (乘2的n次方) >> (除2的n次方) 赋值运算符: 先其他运算再赋值

5 << 2 // 5 --- 101 左移 2 10100 --- 10 5 * 2^ 1

5 >> 2 // 5 --- 101 右移 2 001 --- 1

注: ++,-- 单独语句,不是运算符

数组

var 数组变量名 [元素数量]T // 一旦定义,长度不能变
var a [3]int

数组初始化

var a1 [3]int //数组会初始化为int类型的零值
var a2 = [3]int{1,2} //使用指定的初始值完成初始化
var a3 = [...]int{1, 2} // 让编译器根据初始值的个数自行推断数组的长度
a4 := [...]int{1: 1, 3: 5} // 指定索引值的方式来初始化数组 [0, 1, 0 ,3]

数组遍历

package main

import "fmt"

func main() {
    var a = [...]int{1, 2, 3, 4, 5}
    // for循环遍历
    for i := 0; i < len(a); i++ {
        fmt.Println(a[i])
    }
    // range
    for i, j := range a {
        fmt.Println(i, j)
    }

    b := [...][2]int{
        {1,2},
        {2,1},
    }

    for _, i := range b {
        for _, j := range i {
            fmt.Printf("%d\t", j)
        }
    }
}

数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。

切片slice

切片是引用类型,拥有相同类型元素的可变长度的序列

var 切片名 []切片类型
var name []string
# 动态创建
make([]T, size, cap) // T 类型,size 长度, cap容量(从第一个到最后容量数),cap 可省却
package main

import "fmt"

func main() {
    //var a []int
    var c = []string{"a", "b"}
    fmt.Println(len(c), cap(c))
    d := make([]int, 2, 10)
    fmt.Println(len(d), cap(d))
    e := d[3:6]
    fmt.Println(len(e), cap(e)) // 3, 3-10 7


    s1 := make([]int, 3) // [0,0,0]
    s2 := s1
    // 拷贝前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容
    s2[0] = 100
    fmt.Println(s1)
    fmt.Println(s2)

    // 遍历和for一样
    for i := 0; i < len(s1); i++ {
        fmt.Printf("%d\t", s1[i])
    }
    for _, i := range s2 {
        fmt.Printf("%d\t", i)
    }
}

切片的本质就是对底层数组的封装

append

package main

import "fmt"

func main() {
    var num []int
    for i := 0; i < 20; i++ {
        // 追加一个元素
        num = append(num, i)
        fmt.Printf("%v len:%d cap:%d ptr:%p\n", num, len(num), cap(num), num)
    }
    // 追加多个元素
    num = append(num, 1, 2, 3)
    fmt.Printf("%v, \n", num)
    num1 := []int{99, 98}
    // 追加切片
    num = append(num, num1...)
    fmt.Println(num)
}

copy

copy(目标切片,源切片)
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]

    var a = make([]string, 5, 10) // 已经有5个了
    for i := 0; i < 10; i++ {
        a = append(a, fmt.Sprintf("%v", i))
    }
    fmt.Println(a, cap(a), len(a))

    // [     0 1 2 3 4 5 6 7 8 9] 20 15
}

指针

  • 取地址 &a
  • 根据地址取值 *b

make & new

都用来申请内存

make: slice,map, chan申请内存,返回类型本身
new: 基本数据类型申请内存, 返回指针, 且内存对应的值为类型零值

a := new(int)

map

散列表(hash)实现, 无序的基于key-value的数据结构,引用类型,必须初始化才能使用.

map[KeyType]ValueType

make(map[keytype]valuetype, [cap])

code := make(map[string]string, 10)

code["a"] = "a"
code["b"] = "b"

# 判断 值是否存在
value, ok := map[key]
if ok {
    // 存在
} else {
    // 不存在
}

// 循环
for k, v := range map {
    // k,v v可省略
}

函数

func 函数名(参数)(返回值){

}

示例

func sum1(x, y int) int {
    sum := x + y
    return sum
}

// 返回值定义了返回值名,return可省却
func sum2(x, y int) (sum int) {
    sum = x + y
    return
}

// 可变参数,可变参数可有可无
func sum3(x int, y ...int) {
    fmt.Println(x)
    fmt.Println(y) // 切片
}

func main() {
    r1 := sum1(1, 2)
    r2 := sum2(1, 2)
    fmt.Println(r1, r2)
    sum3(1) // 1 []
    sum3(1, 2) 1 [2]
    sum3(1, 2, 3) 1 [2,3]
}

defer

延迟处理, 多个defer 按照先进后出处理。

函数return分成两步

  • 返回值赋值
  • defer
  • return
package main

import "fmt"

func deferdemo(name string) {
    fmt.Println(name)
}

func f1() int {
    x := 5
    defer func() {
        x++ // 修改x不是 返回值
    }()
    // 返回值赋值 5
    // 修改x值为 6
    // retrun 5
    return x
}

func f2() (x int) {
    defer func() {
        x++
    }()

    // 返回值赋值 x=5
    // 修改x值为 x=6
    // return 6

    return 5 // 返回值
}

func f3() (y int) {
    x := 5
    defer func() {
        x++
    }()

    // y = x = 5
    // x = 6
    // return y = 6

    return x
}

func f4() (x int) {
    defer func(x int) {
        x++
    }(x)
    // x = 5
    // 副本 x = 6
    // return x
    return 5
}

func main() {
    deferdemo("666")
    defer deferdemo("777")
    deferdemo("888")
    defer deferdemo("999")

    fmt.Println(f1(), f2(), f3(), f4()) // 5,6,5,5

}

匿名函数


    // 多次执行
    e1 := func(x, y int) {
        fmt.Println(x)
        fmt.Println(y)
        fmt.Println(x + y)
    }
    e1(2, 4)

    // 立即执行,只执行一次
    func(x, y int) {
        fmt.Println(x * y)
    }(1, 2)

闭包

  1. 函数作为返回值
  2. 外部变量引用
package main

import "strings"

import "fmt"

// checkMail
func checkMail(domain string) func(string) string {
    return func(name string) string {
        if strings.HasSuffix(name, domain) {
            return name
        }
        return name + "@" + domain

    }
}

func main() {
    talkFunc := checkMail("@ysicing.me")
    fmt.Println(talkFunc("i"))
    fmt.Println(talkFunc("root@ysicing.me"))
}

panic/recover

  • recover()和defer一起使用
  • defer在panic语句前
package main

import "fmt"

func funcA() {
    fmt.Println("a")
}

func funcB() {

    defer func() {
        err := recover()
        fmt.Println(err)
    }()
    panic("error")
    fmt.Println("b")
}

func funC() {
    fmt.Println("c")
}

func main() {
    funcA()
    funcB()
    funC()
}

占位符

    m1 := make(map[string]int, 5)
    m1["demo"] = 1
    fmt.Printf("%v\n", m1) // 值的默认格式
    fmt.Printf("%+v\n", m1) // 类似%v, 结构体会加字段名
    fmt.Printf("%#v\n", m1)    // map[string]int{"demo":1} 值Go语法
    fmt.Printf("%T\n", m1) // map[string]int 打印类型

结构体

Copyright © 2020 | 仅供学习参考,不可用于其他途径. all right reserved,powered by Gitbook Updated at 2020-02-28 16:00:55

results matching ""

    No results matching ""