Go语言基础知识(一)

作者: 爪爪熊 | 来源:发表于2017-07-29 17:49 被阅读276次

    Go 语言学习的入门部分,如果有C语言的基础类比学习会非常迅速。总结来说 Go语言 和 C语言很相似,语法更为简单,所以写起来会相对迅速很多。以下是基础语法的笔记。

    >教程来自 http://www.yiibai.com/go/go_start.html
    感谢博主的精心编写。

    导入包

    import "fmt"
    import "math"
    

    或者下面:

    import (
        "fmt"
        "math"
    )
    

    导出名字

    go 中,首字母大写名称是被导出的。全小写的名字是不会被导出。

    fmt.Println(math.Pi)      //这里PI 改为 pi 就是不可以的。
    

    函数

     func add(x int ,y int) int {
        return x + y
    }
    

    或者

    func add(x,y int) int {
        return x + y 
    }
    

    函数中命名返回值

    func split (sum int )(x,y int){
        x = sum * 4 /9 ;
        y = sum - x ;
        return ;
    }
    

    变量

    变量定义看起来颇为随意,很方便。

    var c, python , java = true , false , "no!" 
    var i , j int = 1 , 2 
    
    func main(){
        fmt.Println(i,j,c,python,java)
    }
    

    变量在函数内还可以去掉var等关键字,直接退化为如下操作:

    func main(){
        var i , j int = 1 , 2 
        k := 3
        c , python , java = true , false , "no!"
        fmt.Println(i,j,k,c,python,java)
    }
    

    基本数据类型

    bool
    string
    int  int8  int16  int32  int64
    uint uint8 uint16 uint32 uint64 uintptr
    byte //uint8 别名
    rune //int32 的别名
         //代表一个unicode码
    float32 float64
    complex64 complex128
    

    int,uint,uintptr在32位系统上是32位,而在64位系统上是64位。一般直接使用int就可以了。一般仅有特定理由才用定长整型或者无符号整型。

    var (
        ToBe    bool      = false
        MaxInt uint64     = 1<<64 - 1
        z      complex128 = cmplx.Sqrt(-5 + 12i) 
    )
    
    func main(){
        const f = "%T(%v)\n"
        
        fmt.Printf(f,z,z)
        fmt.Printf(f,MaxInt,MaxInt)
        fmt.Printf(f,ToBe,ToBe)
    }
    

    零值

    变量在定义时候没有明确的初始化时会赋值为零值.

    数值类型为 **0**
    布尔类型为 **false**
    字符串为 ""  **空字符串**
    
    package main
    import "fmt"
    
    func main(){
        var i int
        var f float64
        var b bool
        var s string
        fmt.Printf("%v %v %v %q\n",i,f,b,s)
    
    }
    

    类型转换

    表达式T(v)将值v转换为类型T。

    一些关于数值的转换

    var i int = 42
    var f float64 = float64(i)
    var u uint = uint(f)
    
    //或者更为简单的形式
    i := 42
    f := float64(i)
    u := uint(f)
    

    类型推导

    在定义一个变量却并不显式指定其类型的时候(使用 := 语法或者 var =表达式语法),变量的类型由(等号)右侧的值推导得出.

    当右值定义了类型时候,新变量的类型与其相同:

    var i int
    j := i  // j也是一个int
    

    当时当右边包含了未指明类型的数字常量时候,新的变量就可能是int、float64或complex128 。 这取决于常量的精度:

    i := 42            //int
    f := 3.142         //float64
    g := 0.867 + 0.5i  //complex128
    

    常量

    常量的定义与变量类似,只不过使用 const 关键字。

    常量可以是字符、字符串、布尔或数字类型的值。

    常量不能使用 := 语法定义。

    package main
    
    import "fmt"
    
    const Pi = 3.14
    
    func main(){
        const World = "世界"
        fmt.Println("Hello",World)
        fmt.Println("Happy",Pi,"Day")
        
        const Truth = true
        fmt.Println("Go rules?",Truth)
    }
    

    数值常量

    数值常量是高精度的值。

    一个未指定类型的常量由上下文来决定其类型。

    也尝试一下输出 needInt(Big) 吧。(int 可以存放最大64位的整数,根据平台的不同有时会更少。)

    const(
        Big   = 1  << 100
        small = Big >> 99
    )
    
    func needInt(x int) int {return x * 10 + 1}
    func needFloat(x float64) float64 {
        return x * 0.1
    }
    

    控制流

    for

    Go 只有一种循环结构 --- for循环。这点和C比较相似。具体如下:

    func main(){
        sum : = 0 
        for i := 0 ; i < 10 ; i++ {
           sum += i
        }
        fmt.Println(sum)
    }
    

    其中循环初始化语句和后置语句都是可选的:

    func main(){
        sum := 1 
        for ; sum < 1000 ; {
            sum += sum 
        }
        fmt.Println(sum)
    }
    

    这里和C语言很相似,但是不需要括号,如果有括号反而会报错。

    for 也可以变成 C语言的中while

    func main(){
        sum := 1
        for sum < 1000 {
            sum += sum
        }
        fmt.Println(sum)
    }
    

    或者变为死循环

    func main(){
        for {    //无退出条件,变成死循环
        
        }
    }
    

    if

    就像 for循环一样,Go 的 if语句也不要求用 ()括号括起来,但是 {} 大括号表示作用域的内容还是必须要有的。

    func sqrt(x float64) string {
        if x<0 {
            return sqrt(-x) + "i"
        }
        return fmt.Sprint(math.Sqrt(x))
    }
    

    if的便捷语句

    for语句一样,if语句可以在条件之前执行一个简单的语句。由这个语句定义的变量的作用域仅在 if 范围之内。

    func pow(x,n,lim float64) float64 {
        if v := math.Pow(x,n); v < lim {
            return v
        }
        return lim 
    }
    

    if和else

    if 的便捷语句定义的变量同样可以在任何对应的else块中使用。(提示:两个 pow 都在main 中调用 fmt.Println 前执行完了)

    func pow(x,n,lim float64) float64 {
        if v:math.Pow(x,n); v<lim{
            return v
        } else {
            fmt.Printf("%g >= %g\n",v,lim)
        }
        return lim
    }
    

    swith语句

    switch除非以 fallthrough 语句结束,否则分支会自动终止。

    switch os: rumtime.GOOS ; os{
        case "darwin":
            fmt.Println("OS X")
        case "linux":
            fmt.Println("Linux")
        default:
            //freebsd , openbsd,
            //plan9,windows...
            fmt.Printf("%s.",os)
    }
    

    switch 的执行顺序

    switch 的条件从上到下执行,当匹配成功的时候停止。

    switch i {
        case 0 :
        case f():
    }
    //当i==0时候不会调用 **f()**
    
    today := time.Now().Weekday()
    switch time.Saturday {
        case today + 0 :
            fmt.Println("Today.")
        case today + 1:
            fmt.Println("Tomorrow")
        case today + 2:
            fmt.Println("In two days")
        default:
            fmt.Println("Too far away")
    
    }
    

    没有条件的swtich

    没有条件的switch 同 switch true一样。这一构造使得可以用更清晰的形式来编写长的if-then-else 链。

    t := time.Now()
    switch {
        case t.Hour() < 12 :
            fmt.Println("Good morning!")
        case t.Hour() < 17 :
            fmt.Println("Good afternoon!")
        default:
            fmt.Println("Good evening.")
    }
    

    defer 语句

    defer 语句会延迟函数的执行,直到上层函数返回。延迟调用的参数会立即生成,但是在上层函数返回前函数都不会调用。

    func main(){
        defer fmt.Println("world")
        
        fmt.Println("hello")
    }
    

    defer 栈
      延迟函数调用被压入一个栈中。当函数后进先出的顺序调用被延迟的函数调用。

    fmt.Println("counting")
    
    for i:= 0 ; i<10 ; i++{
        defer fmt.Println(i)
    }
    fmt.Println("done")
    

    指针

    go 具有指针。指针保存了变量的内存地址。

    类型 *T 是执行类型为 T 的指针,其零值是 nil

    var p *int 
    

    &符号会生成一个指向其作用对象的指针。

    i := 42
    p = &i
    

    * 符号表示指针指向的底层的值。

    fmt.Println(*p)   //通过指针p读取i
    *p = 21           //通过指针p设置i
    

    这也就是通常所说的“间接引用”或“非直接引用”。

    与C不同,Go 没有指针运算。

    i,j := 100 , 666
    
    p := &i
    fmt.Println(*p)    //通过p读取I的值
    *p = 777
    fmt.Println(i)
    
    p = &j
    *p = *p / 37 
    fmt.Println(j)
    

    结构体字段

    结构体字段使用点号来访问(这点和C很像)

    type Vertex struct {
        X int
        Y int
    }
    
    func main(){
        v:= Vertex(1,2)
        v.X = 4
        fmt.Println(v.X)
    }
    

    结构指针

    结构体字段可以通过结构体指针来访问。

    通过指针间接的访问是透明的

    type Vertex struct{
        X int
        Y int
    }
    
    func main(){
        v := Vertex{1,2}
        p := &v
        p.X = 1e9
        fmt.Println(v)
    }
    

    结构体符文

    结构体符文表示通过结构体字段的值作为列表来新分配一个结构体。

    使用 Name: 语法可以仅列部分字段。(字段名的顺序无关。)

    特殊的前缀 & 返回一个指向结构体的指针。

    type Vertex struct {
        X,Y int
    }
    
    var (
        v1 = Vertex{1,2}  //类型为 Vertex
        v2 = Vertex{X:1}  //Y:0 被省略
        v3 = Vertex{   }  //X:0 和 Y:0
        p = &Vertex{1,2}  //类型为 *Vertex
    )
    

    数组 (和C语言的数组概念类似)

    类型 [n]T 是一个有 n 个类型为 T 的值的数组。

    表达式

    var a [10]int 
    

    定义变量 a 是一个有十个整数的数组。

    数组的长度是其类型的一部分,因此数组不能改变大小。这看起来是一个制约,但是Go提供一个更加便利的方式来使用数组。

    切片(slice)

    一个 slice 会指向一个序列的值,并且包含了长度信息。

    []T 是一个元素类型为 T 的切片(slice)。

    len(s) 返回 slice s 的长度。

    func main(){
        s := []int{2,3,4,5,11,13}
        fmt.Println("s ==",s)
        for i:= 0 ; i < len(s) ; i++{
            fmt.Printf("s[%d] == %d\n",i,s[i])
        }
    }
    

    slice的切片
      切片slice 可以包含任意的类型,包括另一个 slice

    func main(){
        //create a tic-tac-toe board
        game := [][]string{
            []string{"_","_","_"},
            []string{"_","_","_"},
            []string{"_","_","_"},
        }
        game[0][0] = "X"
        game[2][2] = "O"
        game[2][0] = "X"
        game[1][0] = "O"
        game[0][2] = "X"
        printBoard(game)
    }
    
    func printBoard(s [][]string){
        for i:=0 ; i < len(s); i++{
            fmt.Printf("%s\n",strings.Join(s[i]," "))  //TODO 不懂
        }
    }
    

    对slice切片

    slice可以重新切片,创建新的 slice 值指向相同的数组。

    表达式:

    s[lo:hi]
    

    表示从 lohi-1 的slice元素,含前端,不包含后端。因此:

    s[lo:lo]
    

    是空的,而

    s[lo:lo+1]
    

    具备一个元素。

    func main(){
        s := []int {2,3,5,7,11,13}
        fmt.Println("s == ",s)
        fmt.Println("s[1:4] ==",s[1:4])
        
        //省略下标从0 开始
        fmt.Println("s[:3] == ",s[:3])
        
        //省略上标到 len(s) 结束
        fmt.Println("s[4:] == ",s[4:])
    }
    

    构造 slice
      slice 由函数 make 创建。这会分配一个全是零值的数组并且返回一个 slice 指向这个数组:

    a := make([]int,5) // len(a) = 5
    

    为了指定容量,可传递第三个参数到make :

    b := make([]int,0,5) //len(b) = 0 , cap(b) = 5
    b = b[:cap(b)]       //len(b) = 5 , cap(b) = 5
    b = b[1:]            //len(b) = 4 , cap(b) = 4
    
    以下为示例代码:
    
    func main(){
        a := make([]int,5)
        printSlice("a",a)
        
        b := make([]int,0,5)
        printSlice("b",b)
        
        c := b[:2]
        printSlice("c",c)
        
        d := c[2:5]
        printSlice("d",d)
    }
    
    func printSlice(s string, x []int){
        fmt.Printf("%s len=%d cap = %d %v\n",s,len(x),cap(x),x)
    }
    

    nil slice

    slice 的零值是 nil

    一个 nilslice 的长度和容量是 0

     var z []int
     fmt.Println(z,len(z),cap(z))
     if z == nil{
         fmt.Println("nil!")
     }
    

    向slice添加元素

    slice 的末尾添加元素是一种常见操作,因此 Go 提供了一个内建函数 append 。内建函数的文档对 append 有详细介绍。

    func append(s []T, vs ...T) []T
    

    append 的第一个参数是 s 是一个元素类型为 Tslice ,其余类型为 T 的值将会附加到该 slice 的末尾。

    append 的结果是一个包含原 **slice** 所有元素加上新添加的元素 **slice**。
    

    如果 s 的底层数组太小,而不能容纳所有值的时候,会分配一个更大的数组。返回 slice 会指向这个新分配的数组。

    func main(){
        var a []int
        printSlice("a",a)
    
        a = append(a,0)
        printSlice("a",a)
    
        a = append(a,1)
        printSlice("a",a)
    
        a = append(a,2,3,4)
        printSlice("a",a)
    }
    
    func printSlice(s string, x []int){
        fmt.Printf("%s len=%d cap=%d %v\n", s , len(x) , cap(x) , x)
    }
    

    范围(range)

    for 循环的 range 格式可以对 slice 或者 map 进行迭代循环。

    当使用 for 循环遍历一个 slice 时候,每次迭代 range 将返回两个值。第一个是当前的下标(序号),第二个是该下标所对应元素的一个拷贝。

    var pow = []int{1,2,4,8,16,32,64,128}
    
    func main(){
        for i,v:= range pow{
            fmt.Printf("2 ** %d = %d \n",i,v)
        }
    }
    

    通过赋值给 _ 来忽略序号和值。

    如果只需要索引值,去掉 “,value” 的部分就可以了。

    func main(){
        pow := make([] int,10)
        for i := range pow{
            pow[i] = 1 << uint(i)
        }
        
        for _,value := range pow{
            fmt.Printf("%d\n",value)
        }
    }

    相关文章

      网友评论

      本文标题:Go语言基础知识(一)

      本文链接:https://www.haomeiwen.com/subject/rheclxtx.html