• go语言之行--基础部分


    一、数据类型

    布尔型

    布尔类型 - 由两个预定义常量组成:true、false,默认值为false

    package main
    
    import "fmt"
    
    func main()  {
        var (
            a bool //默认值为false
            b bool = true
        )
        fmt.Println(a,b)
    }

    数字类型

    整型:

    • uint8(无符号 8 位整型 (0 到 255))
    • uint16(无符号 16 位整型 (0 到 65535))
    • uint32(无符号 32 位整型 (0 到 4294967295))
    • uint64(无符号 64 位整型 (0 到 18446744073709551615))
    • int8(带符号 8 位整型 (-128 到 127))
    • int16(带符号 16 位整型 (-32768 到 32767))
    • int32(带符号 32 位整型 (-2147483648 到 2147483647))
    • int64(带符号 64 位整型 (-9223372036854775808 到 9223372036854775807))

    浮点型:

    • float32(IEEE-754 32位浮点型数)
    • float64(IEEE-754 64位浮点型数)
    • complex64(复数:32 位实数和虚数)
    • complex128(复数:64 位实数和虚数)

    其他数字类型:

    • byte(字符类型,存储为ascii码,与uint8相同)
    • rune(与int32相同)
    • uint(32 或 64 位)
    • int(与 uint 大小相同)
    • uintptr(无符号整型,用于存放一个指针) 

    字符串类型

    表示方式:

    • 双引号(" ")
    • 反引号(` `)

    区别:使用反引号定义的字符串保留原始字符(如换行 , 等)会对特殊字符进行转义,打印时候原样输出,而双引号则不转义。

    字符串底层是一个byte的数组。

    字符串本身是不可变的,因此要改变字符串中的字符,需要将字符串转变为数组,修改数组后再将数组转换为字符串:

    package main
    
    import "fmt"
    
    func main()  {
        str := "hello world"
        s := []byte(str)
        s[0] = 'W'
        str = string(s)
        fmt.Println(str)
    
    }//结果 Wello world

    双引号、反引号区别:

    package main
    
    import "fmt"
    
    func main()  {
        var (
            a string =`wd
    `
            b string = "age 
    "
        )
        fmt.Println(a,b)
    }
    //结果
    wd
     age

    派生类型

    • 指针类型
    • 数组类型
    • 结构类型
    • 联合类型
    • 函数类型
    • 切片类型
    • 函数类型
    • 接口类型

    类型转换

    不同的数据类型之间是不允许进行赋值或运算操作,必须经过类型转化才能进行运算或者赋值

    转换方法:数据类型()

    package main
    
    import "fmt"
    
    func main()  {
        var a int32 = 1
        var b int64 = 64
        c:= int64(a)+ b  //不转换编译报错
        fmt.Println(c)
    }

    二、运算符

    算数运算符

    • +相加 :x + y = z
    • -相减 :  x - y = z
    • *相乘:x * y = z
    • % 求余: x % y =z
    • ++ 自增 :x++
    • -- 自减:x--

    关系运算符

    • == 判断两个值是否相等,如果相等返回 True 否则返回 False。
    • != 判断两个值是否不相等,如果不相等返回 True 否则返回 False。
    • > 判断左边值是否大于右边值,如果是返回 True 否则返回 False。
    • < 判断左边值是否小于右边值,如果是返回 True 否则返回 False。
    • >= 判断左边值是否大于等于右边值,如果是返回 True 否则返回 False。
    • <= 判断左边值是否小于等于右边值,如果是返回 True 否则返回 False。

    逻辑运算符

    • && 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。
    • || 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。
    • ! 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。

    位运算符

    位运算符对整数在内存中的二进制位进行操作。

    • & 按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。
    • | 按位或运算符"|"是双目运算符。 其功能是参与运算的两数各对应的二进位相或。
    • ^ 按位异或运算符"^"是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。
    • << 左移运算符"<<"是双目运算符。左移n位就是乘以2的n次方。 其功能把"<<"左边的运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。
    • >> 右移运算符">>"是双目运算符。右移n位就是除以2的n次方。 其功能是把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数。
    //假设A = 60, B = 13;
     二进制格式表示
    
    A = 0011 1100
    
    B = 0000 1101
    
    -----------------
    
    A&B = 0000 1100
    
    A|B = 0011 1101
    
    A^B = 0011 0001

    赋值运算

    • = 简单的赋值运算符,将一个表达式的值赋给一个左值
    • += 相加后再赋值 (C += A 等于 C = C + A)
    • -= 相减后再赋值 (C -= A 等于 C = C - A)
    • *= 相乘后再赋值 (C *= A 等于 C = C * A)
    • /= 相除后再赋值 (C /= A 等于 C = C / A)
    • %= 求余后再赋值 (C %= A 等于 C = C % A)
    • <<= 左移后赋值 (C <<= 2 等于 C = C << 2)
    • >>= 右移后赋值 (C >>= 2 等于 C = C >> 2)
    • &= 按位与后赋值 (C &= 2 等于 C = C & 2)
    • ^= 按位异或后赋值  (C ^= 2 等于 C = C ^ 2)
    • |= 按位或后赋值 (C |= 2 等于 C = C | 2)

    变量运算符

    • & 取变量的地址(&a将给出变量a的实际地址)
    • *取变量的指针(*a 是指向变量a的指针)

     三、流程控制

    if-else

    if condition1 {          
    } else if condition2 {    //else 一定要和大括号在一行
    } else if condition3 {

    } else { 
    }

    for

    一般for循环

    for i := 0; i < 100; i++ {  //语句使用分号隔开
    }

    死循环(类似python中的while)

    for true {
    }
    //或者
    for {
    }

     使用range遍历数组、slice、map、chan等

    package main
    
    import "fmt"
    
    func main()  {
        str := "hell world"
        for k :=range str {  //字符串遍历
            fmt.Printf("index=%d val=%c len=%d
    ",k,str[k],len(str)) //k代表索引、v代表值
        }
    
    }//结果 index=0 val=h len=10 ....
    索引循环
    package main
    
    import "fmt"
    
    func main()  {
        str := "hell world"
        for index,val :=range str {  //字符串遍历
            fmt.Printf("index=%d val=%c len=%d
    ",index,val,len(str)) //k代表索引、v代表值
        }
    
    }//结果 index=0 val=h len=10 ....
    同时循环index,val

    switch

    switch条件控制,当条件匹配当前case时候,执行当前case,不会进行往下执行,若都没有匹配到,则执行default,当然也可以使用fallthrought关键字强制执行下面语句。

    switch var {
    case 条件一:
    case 条件二:
    case 条件三:
    default:
    }
    package main
    
    import "fmt"
    
    func main()  {
        var a = 0
        switch a {
        case 0:
            fmt.Println("this is 0")
        case 1:
            fmt.Println("this is 1")
        case 2:
            fmt.Println("this is 2")
        default:
            fmt.Print("this is default")
        }
    }//结果  this is 0
    switch 简单示例
    package main
    
    import "fmt"
    
    func main()  {
        var a = 0
        switch  { //这里没有变量
        case a > 0 && a <3:  //a in 0-3
            fmt.Println("a in 0-3")
        case a < -1 || a > 3:
            fmt.Println("a <0 or a > 3")
        case a == 0:
            fmt.Println("a equal 0")
        default:
            fmt.Print("this is default")
        } //结果 a equal 0
    }
    多条件判断case
    package main
    
    import "fmt"
    
    func main()  {
        var a = 0
        switch  {
        case a > 0 && a <3:  //a in 0-3
            fmt.Println("a in 0-3")
        case a < -1 || a > 3:
            fmt.Println("a <0 or a > 3")
        case a == 0:
            fmt.Println("a equal 0")
            fallthrough //使用fallthrought关键字当满足该条件时候,继续执行下面语句
        default:
            fmt.Printf("this is default
    ")
        } 
    //结果 a equal 0    
    //this is default
    
    }
    加入fallthrought

     goto&label

    label作为一个标记,语法是字符串+冒号定义,在循环中使用lable可调至label的定义的位置

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main()  {
    
        LABLE:  //定义名称为LABLE
        //fmt.Println("回到label")
        for i := 0; i < 10; i++ {
            fmt.Println("-->",i)
            for true {
                i++
                if i == 6 {
    
                    time.Sleep(time.Second * 5)
                    break LABLE  //跳出循环到LABLE定义的地方
                }
                fmt.Println(i)
            }
        }
    
    }

    goto作用在于跳转到label标签定义的地方继续执行。上述代码修改为goto,将是死循环

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main()  {
    
        LABLE:  //定义名称为LABLE
        //fmt.Println("回到label")
        for i := 0; i < 10; i++ {
            fmt.Println("-->",i)
            for true {
                i++
                if i == 6 {
    
                    time.Sleep(time.Second * 5)
                    goto LABLE  //跳出循环到LABLE定义的地方
                }
                fmt.Println(i)
            }
        }
    
    }//结果:每5秒打印1,2,3,4,5

    四、函数

    函数是go语言编程的核心。特点:

    • 不支持重载,一个包不能有两个名称一样的函数
    • 函数也是一种类型,一个函数可以赋值给变量(该特性和python一样)
    • 函数也可以不用起名称,这种函数称为匿名函数
    • go语言函数可以有多个返回值
    • 使用_标识符可忽略返回值

    函数定义语法:

    func 函数名( 参数 ) [(返回值列表)] {
       函数体
    }// [] 代表非必需
    package main
    
    import (
        "fmt"
    )
    
    
    func add(a int,b int) int {
    
        return a + b
    }
    func main()  {
    
            c := add
            d := c(1,2)  // 加括号代表调用函数
            fmt.Println(d)
        }//结果:3
    函数作为变量demo
    package main
    
    import (
        "fmt"
    )
    
    
    func add(a int,b int) (int,int) {
    
        return a + b,a
    }
    func main()  {
    
            c := add
            d , _ := c(1,2)  //使用_忽略第二个返回值
            fmt.Println(d)
        }//结果:3
    忽略返回值demo

    函数参数传递方式

    值传递:一般基本的数据类型都是值传递,如数字、字符串。

    引用传递:复杂的数据类型,如map、chan、interface,是引用传递。

    注意:无论是值传递还是引用传递,传递给函数的都是变量的副本,不过值传递是值的拷贝,引用传递拷贝的是地址,一般来说地址拷贝更为高效。而值拷贝取决于拷贝对象大小,对象越大,则性能相对低。

    函数返回值命名

    go 函数的返回值或者结果参数可以指定一个名字(名字不是强制的),并且像原始的变量那样使用,就像输入参数那样。如果对其命名,在函数开始时,它们会用其类型的零值初始化;如果函数在不加参数的情况下执行了 return 语句,结果参数的当前值会作为返回值返回。

    package main
    
    import "fmt"
    //斐波那契数列实现
    func Factorial(x int) (result int) {  //给函数返回值命名为result
        if x <= 1  {
            result = 1
        } else {
            result =Factorial(x - 2)  + Factorial(x - 1)
            }
    
        return
    }
    func main()  {
        b := Factorial(4)
        fmt.Println(b)
        }//结果:5

    匿名函数

    匿名函数(对比javascript)即没有函数名的函数,只能放在函数中,可以实现函数嵌套定义的功能。

    package main
    
    import "fmt"
    
    func main()  {
        c := func(a int,b int ) int {
            return a + b
        }(3,4)
        fmt.Println(c)
        }//结果:7

    函数的可变参数

    go语言中,也可以像python一样传递可变参数(意思是传递时候才确定有几个参数,定义时候不知道参数个数),可以认为参数是数组形式:

    func funcname(arg...type) { }
    package main
    
    import "fmt"
    
    func add(a int, arg...int) int {
        var sum = 0
        for i := 0; i < len(arg); i++ {
            sum += arg[i] //取参数的值
        }
        return sum
    }
    func main()  {
        c := add(1)  //传递一个参数
        d := add(2,3,4)  //传递两个参数
        fmt.Println(c,d)
        }//结果:0,7

    defer

    当函数中存在defer关键字时候,一旦函数返回则执行defer语句,因此,defer可以做一些资源清理的事情。

    特性:

    • 多个defer语句,按照后进先出的方式执行。
    • defer语句中的变量,在defer声明时候就决定了。
    package main
    
    import "fmt"
    
    func main()  {
        i := 0
        defer fmt.Println(i)   // 放入栈
        i++
        defer fmt.Println(i)  //在放入栈,先执行
        fmt.Println("wd")
        return
    
        }//结果:wd ,1,0

    命令行参数

    概念:

    • 命令行参数(或参数):是指运行程序时提供的参数。
    • 已定义命令行参数:是指程序中通过 flag.Xxx 等这种形式定义了的参数。输入参数时需要 -flag 形式。
    • 非 flag(non-flag)命令行参数(或保留的命令行参数):不符合 -flag 形式的参数。- 、--和 --flag 都属于 non-flag 参数。

    来自命令行的参数在程序中通过os.Args(对比python的sys.agrv) 获取,导入包 os 即可。其中 os.Args[0] 为执行的程序名,os.Args[1] ~ os.Args[n-1] 是具体的参数。

    //示例
    test.exe  1 2 
    //test.exe 为os.Args[0] ,1是 os.Args[1],2是os.Args[2]

    go语言的初始化顺序

     初始化顺序规则: 引入的包 -->包中的常量、变量、init-->当前包的常量、变量、init -->main函数

    图示:

    注意事项:

    • 当前go源文件中, 每一个被Import的包, 按其在源文件中出现顺序初始化。
    • 如果当前包有多个init在不同的源文件中, 则按源文件名以字典序从小到大排序,小的先被执行到, 同一包且同一源文件中的init,则按其出现在文件中的先后顺序依次初始化; 当前包的package level变量常量也遵循这个规则; 其实准确来说,应是按提交给编译器的源文件名顺序为准,只是在提交编译器之前, go命令行工具对源文件名按字典序排序了。
    •  init只可以由go runtine自已调用, 我们在代码中不可以显示调用,也不可以被引用,如赋给a function variable。
    • 包A 引入包B , 包B又引入包C, 则包的初始化顺序为: C -> B -> A
    • 引入包,必须避免死循环,如 A 引 B , B引C, C引A.
    •  一个包被其它多个包引入,如A -> B ->C 和 H -> I -> C , C被其它包引了2次, 但是注意包C只被初始化一次。
    • 另一个大原则, 被依赖的总是先被初始化,main包总是被最后一个初始化,因为它总是依赖别的包。
  • 相关阅读:
    volatile 和 mutable 关键字
    字符串第一个只出现一次的字符
    学一下HDFS,很不错(大数据技术原理及应用)
    把数组排成最小的数
    求第K大的数字
    数组中超过一半的数字
    打印字符的任意排列
    实时推荐系统架构
    带有任意指向指针的链表进行复制
    surfaceflinger中各个layer的排序
  • 原文地址:https://www.cnblogs.com/wdliu/p/9183267.html
Copyright © 2020-2023  润新知