• Go语言--基础语法笔记


    ###

    换了工作,好久没有添加新文章了,本来是想更新到github上的,想想还是在博客里放着,感觉以前的文章都没有很仔细,都只是问题处理的记录,

    以后想新加一些整理的笔记也好

    ### 

    主要内容

    2.1变量

    2.2数据类型

    2.3数据类型相互转换

    2.4指针

    2.5变量生命期

    2.6字符串应用

    2.7枚举

    2.8类型别名

    2.1变量

    2.1.1 声明变量

    功能:存储用户的数据

    注意: 变量必须经过声明才能开始使用

    变量声明格式:

    1. 标准格式

    var 变量名 变量类型

    以关键字var开头,后置变量类型,行尾无线分号

    package main
    import ("fmt")
    func main() {
         var a int
         var b string
         var c []float32
         var d func() bool
         var e struct{
            x int
         }
    } 
    1. 批量格式

    使用var关键字 和括号

    package main
    import ("fmt")
    func main() {
         var (
            a int
            b string
            c []float32
            d func() bool
            e struct{
                x int
            }
         )
    }
    

      

    2.1.2 初始化变量

    • 整型和浮点型变量默认值:0

    • 字符串变量的默认值空字符串

    • 布尔型默认值为bool

    • 切片、函数、指针变量的默认值为nil

    1.标准格式

    var 变量名 变量类型 = 表达式

    var hp int = 100

    2.编译器推导的类型

    var hp1 = 100

    标准格式基础上,省略int,编译器推导

    var attack = 40
    var defence = 20
    var damageRate float32 = 0.17
    var damage = float32(attack-defence) * damageRate
    fmt.Println(damage)
    // 3.4
    

      

    3.短变量声明并初始化

    hp := 100

    注意:变量已经被声明过了,再次声明并赋值,使用短变量声明会编译报错

    var p string
    p := '123'
    fmt.Println(p)
    // 错误信息:no new variables on left side of :=(44.4)
    // var p 声明了p变量, p := '123' 会再次声明并赋值
    hp3 := 50
    fmt.Println(hp3)
    

      

    2.1.3 多个变量同时赋值

    顺序:从左到右

    var a int = 100
    var b int = 200
    ​
    a, b = b, a
    fmt.Println(a, b) //200 100
    

      

    2.1.4 匿名变量

    匿名变量用一个"_"下滑线表示

    只需要在变量声明的地方,用下划线代替即可

    package main
    import ("fmt")
    func main() {
        a1, _ := getData()
        _, b1 := getData()
        fmt.Println(a1, b1)
    }
    ​
    type IntSlice []int
    // 编写一个len方法,提供切片的长度
    func (p IntSlice) Len() int { return len(p)}
    // 根据提供i,j元素索引,两个元素进行比较,返回比较结果
    func (p IntSlice) Less(i, j int) bool { return p[i] < p[j]}
    // 根据提供i,j元素索引,交换两个元素的值
    func (p IntSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i]}
    ​
    func getData() (int, int) {
        return 200, 100
    }
    

      

    2.2 数据类型

    2.2.1 整型

    两种: 长度 int8 int16 int32 int64 无符号 uint8 uint16 uint32 uint64

    2.2.2 浮点型

    两种:float32和float64

    fmt.Println("hello world")
    fmt.Printf("%f
    ", math.Pi)
    fmt.Printf("%.2f
    ", math.Pi)
    

      

    2.2.3 布尔型

    注意:go不允许将整型类型转换为布尔型, 无法参与数值运算,也无法跟其他类型进行转换

    var n bool
    fmt.Println( int(n) * 2 ) 
    //cannot convert n (type bool) to type int
    

      

    2.2.4 字符串

    用双引号括起来的内容就是为字符串的内容

      str := "hello world string"
      ch := "中文"
      fmt.Println(str, ch)
        //hello world string 中文
    

      

    • 字符串转义符

      符合说明
      回车
      换行符
      制表符
      ' 单引号
      " 双引号
      双斜杠
      fmt.Println( "str := "c:\Go\bin\go.exe" ")
      str := "c:Goingo.exe" 
      

        

    • 字符串实现基于utf-8编码

    • 定义多行字符串

    const longstr = `
        第一行
        第二行
        
    
        。。。
        
        `
        fmt.Println(longstr)
        
        //第一行
        //第二行
        //
    
        //。。。
    ​
        const codeTemplate = ` // Generated by github.com/davyxu/cellnet/protoc-gen-msg
        // DO NOT EDIT!{{range.Protos}}
        // Source:{{.Name}} {{end}}
        
        package {{.PackageName}}
        {{if gt .TotalMessages 0}}
        import (
            "github.com/davyu/cellnet"
            "reflect"
            _ "github.com/davyxu/cellnet/codec/pb"
        )
        {{end}}
        
        func init() {
            {{range .Protos}}
            //{{.Name}}{{range .Messages}}
            cellnet.RegisterMessageMeta("pb", "{{.FullName}}")
            reflect.TypeOf((*{{.Name}})(nil)).Elem(), {{.MsgID}}) {{end}}
            {{end}}
        }
        `
        fmt.Println(codeTemplate)
    

      


    2.2.5 字符

    字符串中每一个元素都叫字符 两种: 一种为uint8类型 或是 byte类型 代表ascll码的一个字符 另一种为rune类型, 代表一个UTF-8类型,实际为int32 用于处理中文、日文等复合字符 使用fmt.Printf 中“%T”输出变量实际类型,可用于查看byte和rune类型

    var a byte = 'a'
    fmt.Printf("%d %T
    ", a, a)
    var b rune = '中'
    fmt.Printf("%d %T
    ", b, b)
    //97 uint8
    //20013 int32
    

      

    2.2.7 切片 -- 能动态分配的空间

    一个拥有相同类型元素的可变长的序列

    var name []T

    T 代表切片元素类型, 即可以整型、浮点型、布尔型、切片、map、函数等

    t := make([]int, 5)
    t[0] = 1
    t[1] = 2
    t[3] = 3
    fmt.Println(t)
    //[1 2 0 3 0]
        
    str1 := "hello world"
    fmt.Println(str1[6:])
    //world
    

      

    2.3 转换不同的数据类型

    格式:

    T(表达式) 输出各数值范围

    package main
    ​
    import (
        "fmt"
        "math"
    )
    ​
    func main() {   
        fmt.Println("int8 range:", math.MinInt8, math.MaxInt8)
        fmt.Println("int16 range:", math.MinInt16, math.MaxInt16)
        fmt.Println("int32 range:", math.MinInt32, math.MaxInt32)
        fmt.Println("int64 range:", math.MinInt64, math.MaxInt64)
        int8 range: -128 127
        int16 range: -32768 32767
        int32 range: -2147483648 2147483647
        int64 range: -9223372036854775808 9223372036854775807
    ​
        //初始化一个32位整型值
        var a1 int32 = 1047483647
        fmt.Printf("int32: 0x%x %d
    ", a1, a1)
        //nt32: 0x3e6f54ff 1047483647
        //int32 转为 int16, 发生数值截断
        b1 := int16(a1)
        fmt.Printf("a1 int16: 0x%x %d
    ", b1, b1)
        //a1 int16: 0x54ff 21759
        
        //将常量保存为float32类型
        var c float32 = math.Pi
        //转为int类型,浮点数发生精度丢失
        fmt.Println(int(c))
        //3
    }
    

      

    2.4 指针

    两个核心:

    一种是类型指针,允许对这个指针类型的数据进行修改。 传递数据使用使用指针,而无须拷贝数据 类型指针不能进行偏移和运算

    二种是切片, 由指向起始元素的原始指针、元素数量和容量组成

    2.4.1 认识指针地址和指针类型

    每个变量在运行时都会被内存分配一个地址,这个地址代表变量在内存中的位置 使用“&”操作符放在变量前面对变量进行“取地址”操作 格式:

         ptr := &variable   //variable的类型为T

    其中v代表被取地址的变量,被取地址的variable使用ptr变量进行接收,ptr的类型为“T”,称作T的指针类型。“”代表指针

    package main
    ​
    import (
        "fmt"
        "math"
    )
    ​
    func main() {   
        var cat int = 1
        var str2 string = "banana"
        fmt.Printf("%p, %p
    ", &cat, &str2)
    }
        //0xc00004e0e8, 0xc0000421f0 为cat,str2取地址后的指针值
    

      

    注意:变量、指针和地址三种的关系是:每个变量都拥有地址,指针的值就是地址

    2.4.2 从指针获取指针指向的值

    对变量“&”取地址操作后获得这个变量的指针,对指针使用“*”操作,就是指针取值

    package main
    ​
    import (
        "fmt"
        "math"
    )
    func main() {
        var house = "Malibu Point 10880, 90265"
        // 对字符串取地址,ptr1类型为*string
        ptr1 := &house
        // 打印ptr类型
        fmt.Printf("ptr1 类型:%T
    ", ptr1)
        // 打印ptr指针地址
        fmt.Printf("ptr1 地址:%p
    ", ptr1)
        // 对指针进行取值操作
        value := *ptr1
        // 取值后类型
        fmt.Printf("value 类型:%T
    ", value)
        // value值
        fmt.Printf("value:%s
    ", value)
    }
    // ptr1 类型:*string
    // ptr1 地址:0xc000042200
    // value 类型:string
    // value:Malibu Point 10880, 90265

    总结:

    取地址“&”和取值“”是一对互补操作符,“&”取地址,"&"根据地址取出地址指向的值

    1.对变量进行其地址(&)操作,可获得这个变量的指针变量

    2.指针变量的值是指针地址

    3.对指针变量进行取值()操作,可以获得指针变量指向的原变量的值

    2.4.3 使用指针修改值

    x, y := 1,2

    package main
    ​
    import (
        "fmt"
        "math"
    )
    ​
    func main() {
        //错误示例
        swap1(&x, &y)
        fmt.Println("x: ",x, "y:", y)
        //x:  1 y:  2
        //正确
        swap(&x, &y)
        fmt.Println("x: ", x, "y: ",y)
        //x:  2 y:  1
    }
    // 交换函数
    func swap(a, b *int) {
        // 取a的指针的值,赋给临时变量t
        t := *a
        //取b指针的值,赋值给a指针指向的变量
        *a = *b
        //a指针的值赋值给b指针指向的变量
        *b = t
    }
    ​
    // 错误示例
    func swap1(a, b *int) {
        b, a = a, b
    }
    

      

    2.4.5 创建指针的另一种方法--new()函数

    new(类型)

    str3 := new(string)
    *str3 = "ninja"
    fmt.Println(*str3)
    fmt.Println(str3)
    //ninja
    //0xc000042230

    2.6 字符串应用

    2.6.1 计算机字符串长度 -- len()

    go 语言字符串都是以UTF-8格式保存,每个中文占用3个字符

    tip1 := "genji is a ninja"
    fmt.Println(len(tip1))
    // 16
    tip2 := "忍者无敌"
    fmt.Println(len(tip2))
    //12
    // 使用RuneCountInString()统计Uncode字符数量
    fmt.Println(utf8.RuneCountInString("忍者"))
    

    总结

    • ASCII字符串长度使用len()函数

    • Unicode字符串长度使用utf8.RuneCountInString()函数

     

    2.6.2 遍历字符串 -- 获取每个字符串

    两种写法

    1. 遍历每一ASCII字符, 使用for循环遍历

    theme := "阻击 start"
    ​
        for i := 0; i < len(theme); i++ {
    ​
            fmt.Printf("ascii: %c %d
    ", theme[i], theme[i])
    ​
        }
        // ascii: é 233
        // ascii: • 152
        // ascii: » 187
        // ascii: å 229
        // ascii: • 135
        // ascii: » 187
        // ascii:   32
        // ascii: s 115
        // ascii: t 116
        // ascii: a 97
        // ascii: r 114
    

      

    1. 按Unicode字符遍历字符串  

      for _, s := range theme {
    ​
            fmt.Printf("Unicode %c %d
    ", s, s)
    ​
        }
    ​
        // Unicode 阻 38459
        // Unicode 击 20987
        // Unicode   32
        // Unicode s 115
        // Unicode t 116
        // Unicode a 97
        // Unicode r 114
        // Unicode t 116
    

      

    总结:

    • ASCII字符串遍历直接使用下标

    • Unicode字符串遍历使用for range

    2.6.3 获取字符串的某一段字符

    string.Index() 在字符串中搜索另一个子串

    tracer := "努力拥抱每一天,不断成长"
    comma := strings.Index(tracer, "每一天")
    posi := strings.Index(tracer[comma:], "成长")
    ​
    fmt.Println(comma, posi, tracer[comma+posi:])
    ​
     // 12 18 成长
    

     

    总结:

    • strings.Index:正向搜索子字符串

    • string.LastIndex: 反向搜索自字符串

    搜索的起始位置可以通过切片偏移制作

    2.6.4 修改字符串

    go语言无法直接修改每一个字符元素,只能通过重新构造新的字符串并赋值给原来的字符串变量

    angel := "Hero nerver die"
    ​
    arrayBytes := []byte(angel)
    ​
    for i := 5; i <= 10; i++ {
    ​
         arrayBytes[i] = '-'
    ​
    }
    ​
    fmt.Println(arrayBytes)
    ​
    // [72 101 114 111 32 45 45 45 45 45 45 32 100 105 101]
    ​
    fmt.Println(string(arrayBytes))
    ​
     // Hero ------ die
    

      

    总结

    • Go语言的字符串是不可以改变的

    • 修改字符串时,可以将字符串转换为[]byte进行修改

    • []byte 和string 可以通过强制类型转换互换

     

    2.6.5 连接字符串

    可以使用加号“+”连接 可以使用类似于StringBuilder的机制连接,更高效

    hamer := "GO GO GO"
    sickle := "You Can"
    ​
    // 声明字节缓冲
    ​
    var stringBuilder bytes.Buffer
    ​
    // 将字符串写入缓冲区
    ​
    stringBuilder.WriteString(hamer)
    ​
    stringBuilder.WriteString(sickle)
    ​
    //将缓冲以字符串形式输出
    ​
    fmt.Println(stringBuilder.String())
    ​
    // GO GO GOYou Can
    

      

    • bytes.Buffer可以缓冲并写入各种字节数组,字符串也是一种字符串数组,使用writeString()

    • 将需要连接的字符串,通过bytes.Buffer声明缓冲stringBuilder调用WriteString()方法写入里面,

    • 再通过stringBuilder.String()方法将缓冲转换为字符串

    2.6.6 格式化

    写法: fmt.Sprintf(格式化样式,参数列表)

    格式化样式:字符串形式,格式化动词以%开头

    参数列表:多个参数以逗号分隔,个数必须与格式化中样式个数一一对应

     var progress = 2
        var target = 8
    ​
        // 两参数格式化
        title := fmt.Sprintf("以完成%d个任务,还差%d个就完成", progress, target)
    ​
        fmt.Println(title)
        // 以完成2个任务,还差8个就完成
    ​
        pi := math.Pi
    ​
        // 按数值本身格式输出
        variant := fmt.Sprintf("%v %v %v", "月球基地", pi, true)    
        fmt.Println(variant)
        // 月球基地 3.141592653589793 true
    ​
        profile := &struct {
            Name string
            HP   int
        }{
            Name: "stat",
            HP: 150,
        }
    ​
        fmt.Printf("使用'%%+v' %+v
    ", profile)
        fmt.Printf("使用'%%#v' %#v
    ", profile)
        fmt.Printf("使用'%%T' %T
    ", profile)
    ​
        // 使用'%+v' &{Name:stat HP:150}
        // 使用'%#v' &struct { Name string; HP int }{Name:"stat", HP:150}
        // 使用'%T' *struct { Name string; HP int }
    

      

    base64编码解码示例

    package main
    ​
    import (
        "fmt"
        "encoding/base64"
    )
    ​
    func main() {
    ​
        // 需要处理的字符串
        message := "Away from keyboard. https://golang.org/" 
    ​
        // 编码消息, 传入的字符串需转为字节数组,才能供这个函数使用
        encodeMessage := base64.StdEncoding.EncodeToString([]byte(message))
        // 输出编码完成的消息
        fmt.Println(encodeMessage)
        // 解码消息
        data, err := base64.StdEncoding.DecodeString(encodeMessage)
        // 出错处理
    ​
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println(string(data))
        }
    ​
        // QXdheSBmcm9tIGtleWJvYXJkLiBodHRwczovL2dvbGFuZy5vcmcv
        // Away from keyboard. https://golang.org/
    ​
    ​
    }
    

      

     

    2.7 枚举

    2.7.1 枚举 -- 一组常量值

      // 使用iota模拟枚举
      type Weapon int
       const (
           Arrow Weapon = iota // 开始生成枚举值,默认为0
            Shuriken
           SniperRifle
           Rifle
           Blower
       )
       // 输出所有枚举值
        fmt.Println(Arrow, Shuriken, SniperRifle, Rifle, Blower)
       var weapon Weapon = Blower
        fmt.Println(weapon)
        // 0 1 2 3 4
        // 4

    2.7.2 枚举--将枚举值转换为字符串

    package main
    import ("fmt")
    ​
    // 声明芯片类型
    type ChipType intconst (
        None ChipType = iota
        GPU
        CPU
    )
    ​
    func (c ChipType) String() string {
    ​
        switch c {
        case None:
            return "None"
        case GPU:
            return "GPU"
        case CPU:
        return "CPU"
    ​
         }
        return "N/A"
    ​
    }
    ​
    func main() {
        // 输出CPU的值并以整型格式显示
        fmt.Printf("%s %d", CPU, CPU)
        //CPU 2
    }

    2.8 类型别名

    2.8.1 区分类型别名与类型定义

    类型别名的写法:

    type TypeAlias = Type

    类型别名规定:

    TypeAlias只是Type的别名,本质上TypeAlias与Type是同一个类型

    // 将NewInt定义为int类型
    type NewInt int
    // 将int取一个别名叫IntAlias
    type IntAlias = int// 将a声明为一个NewInt类型
    var alias_a NewInt
    fmt.Printf("a type: %T
    ", alias_a)
    // a type: main.NewInt
    // 将a2声明为IntAlias类型
    var alias_a2 IntAlias
    fmt.Printf("a2 type: %T
    ", alias_a2)
     // a2 type: int 

    2.8.2 非本地类型不能定义方法

    不能为不在同一个包中声明的类型定义方法,即不能为在其他包声明的类型在本地包中定义方法
    
    package main
    ​
    import ("time")
    ​
    // 2.8.2
    // 定义time.Duration 的别名为MyDuration
    type MyDuration = time.Duration
    ​
    // 为MyDuration 添加一个函数
    func (m MyDuration) EasySet(a String) {
        
    }
    ​
    func main() {
        
    }
    //# 2-base/2.2-data_type
    //.data_type.go:51:6: cannot define new methods on non-local type time.Duration
    //.data_type.go:51:31: undefined: String
    //exit status 2
    //Process exiting with code: 1

    2.8.3 在结构体成员嵌入时使用别名

    package main
    ​
    import (
        "fmt"
        "reflect"
    )
    ​
    // 定义商标结构
    type Brand struct {
    ​
    }
    // 为商标结构添加Show()方法
    func (t Brand) Show() {
    ​
    }
    ​
    // 为Brand定义一个别名
    type FakeBrand = Brand
    // 定义车辆结构,嵌入商标结构
    type  Vehicle struct {
        Brand
        FakeBrand
    }
    ​
    func main() {
    ​
        // 声明变量 a 为车辆类型
        var a Vehicle
        // 指定调用FakeBrand的Show
        a.FakeBrand.Show()
        // 取a的类型反射对象
        ta := reflect.TypeOf(a)
    ​
        // 遍历a的所有成员
        for i := 0; i < ta.NumField(); i++ {
            // ta 成员信息
            f := ta.Field(i)
            // 打印成员的字段名和类型
            fmt.Printf("FieldName: %v, FieldType: %v
    ", f.Name, f.Type.Name())
        }
    ​
        
        // FieldName: Brand, FieldType: Brand
        // FieldName: FakeBrand, FieldType: Brand
    ​
    ​
    }

    总结:

    • FakeBrand是Brand的一个别名,在Vehicel中嵌入FakeBrand和Brand,Vehicel的类型会以名字的方式保留在Vehicle的成员中

    • FakeBrand和Brand 都有Show()方法, 调用时必须制定调用谁的, a.FakeBrand.Show()

     

     

     

  • 相关阅读:
    OSPF LSA过滤简述 转自 huawei企业论坛
    distribute-list分发列表 转自 红茶三杯sina blog
    rip 协议号
    Ping命令的工作过程及单向Ping通的原因
    Cisco路由器故障诊断技术专题
    self-introduction for bleum (not in use)
    智能手环是什么情况
    页面置换算法 FIFO NUR LRU LFU
    慢慢来----由易语言想到的一点点
    ssh免密登录
  • 原文地址:https://www.cnblogs.com/smallyi/p/9919867.html
Copyright © 2020-2023  润新知