go 基础

作者: 名字刚好七个字 | 来源:发表于2018-03-18 13:05 被阅读0次

    安装和环境配置

    自行百度解决

    go项目的目录结构

    go命令依赖一个重要的环境变量:$GOPATH
    一般的,一个Go项目在GOPATH下,会有如下三个目录:

    |--bin
    |--pkg
    |--src
    

    其中,bin存放编译后的可执行文件;pkg存放编译后的包文件;src存放项目源文件。一般,bin和pkg目录可以不创建,go命令会自动创建(如 go install),只需要创建src目录即可。

    go基础数据类型类型

    bool 类型

    true false

    数字类型

    整型

    无符号:uint8 uint16 uint32 uint64
    有符号:int8 int16 int32 int64

    浮点型

    float32 float64

    复数型

    complex64 complex128

    一些别称类型

    byte -- uint8
    rune -- int32
    uint int 对应CPU平台机器字大小的无符号和有符号类型
    uintptr 无符号整形,用于存放一个指针,一般用于底层和C语言对接

    string类型

    字符串是一个不可改变的字节序列,可以包含任意数据。文本字符串通常被解释为采用UTF-8编码的Unicode码点半序列。

    string的操作

    • 内置函数len()返回一个字符串中的字节数目
    • 索引操作s[i]返回第i个字节的字节值,i必须满足0 ≤ i< len(s)条件约束,否则会引发一个panic
    • 子字符串操作s[i:j]基于原始的s字符串的第i个字节开始到第j个字节(并不包含j本身)生成一个新字符串。参见python的切片操作
    • '+' 操作符可以将两个字符串拼接在一起构造一个新的字符串
    fmt.Println("goodbye" + ", world") // "goodbye, world"
    
    • 字符串可以用==和<进行比较;比较通过逐个字节比较完成的,因此比较的结果是字符串自然编码的顺序
    • 字符串的值不可变,尝试修改字符串内部数据的操作会引发一个error
    • 字符串变量的是可变的
    • 字符串处理常用的标准库包bytes、strings、strconv、unicode

    常量与变量

    常量 一个简单值的标识符,在程序运行时不会被修改的量,数据类型只可能是布尔型、数字型和字符串型。定义格式如下:

    const identifier [type] = value
    
    • iota常量 自行研究

    变量

    声明方法

    var v_name v_type
    var v_name = value
    v_name := value
    

    第一种,指定变量类型,声明后若不赋值,使用默认值。
    第二种,根据值自行判定变量类型。
    第三种,省略var, 注意 :=左侧的变量不应该是已经声明过的,否则会导致编译错误。注意,第三种简短声明形式只能用于函数内,全局变量不允许使用这种声明形式。

    值类型和引用类型

    基本数字类型和布尔类型都是值类型,这些类型的变量直接指向存在内存中的值。用等号进行赋值操作时,实际上是对值进行了拷贝。
    一个引用类型的指针指向的是地址。

    ‘_’标识符

    次标识符用于抛弃值,因为在Go语言中,某一变量被声明但是未被调用会报错,所以引入此变量可以规避这个问题。

    基础结构

    代码组成

    package main
    
    import "fmt"
    
    func main() {
       fmt.Println("Hello, World!")
    }
    

    go语言基础组成分为以下几部分:

    • 包声明
      第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
    • 引入包
      import "fmt" 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数
    • 函数
      下一行 func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)

    执行流程

    执行流程

    包的概念

    包是结构化代码的一种方式:每个程序都由包(通常简称为 pkg)的概念组成,可以使用自身的包或者从其它包中导入内容。
    如同其它一些编程语言中的类库或命名空间的概念,每个 Go 文件都属于且仅属于一个包。
    在 Go 的安装文件里包含了一些可以直接使用的包,即标准库。
    Go 中的包模型采用了显式依赖关系的机制来达到快速编译的目的,如果 A.go 依赖 B.go,而 B.go 又依赖 C.go:
    编译 C.go, B.go, 然后是 A.go.
    为了编译 A.go, 编译器读取的是 B.o 而不是 C.o.
    这种机制对于编译大型的项目时可以显著地提升编译速度。
    如果导入一个包但是没有使用,在编译时会引发错误。

    可见性

    参见C++的公私有成员。
    当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的 代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 private )。

    go系统工具

    常用的命令工具

    好像都挺常用的,主要介绍以下几个:

    • go build
      go build 命令主要是用于测试编译。在包的编译过程中,若有必要,会同时编译与之相关联的包。
      如果是普通包,当你执行go build命令后,不会产生任何文件。
      如果是main包,当只执行go build命令后,会在当前目录下生成一个可执行文件。
      go build 会忽略目录下以”_”或者”.”开头的go文件。
    • go fmt
      go fmt 命令主要是用来帮你格式化所写好的代码文件。
    • go get
      go get 命令主要是用来动态获取远程代码包的。
      go get 命令本质上可以理解为:首先通过源码工具clone代码到src目录,然后执行go install。

    复合数据类型

    以不同的方式组合基本类型可以构造出来的复合数据类型。
    主要讨论四种类型——数组、slice、map和结构体。
    数组和结构体是聚合类型;它们的值由许多元素或成员字段的值组成。数组是由同构的元素组成——每个数组元素都是完全相同的类型——结构体则是由异构的元素组成的。数组和结构体都是有固定内存大小的数据结构。相比之下,slice和map则是动态的数据结构,它们将根据需要动态增长。

    数组

    数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。

    • 数组的每个元素可以通过索引下标来访问,索引下标的范围是从0开始到数组长度减1的位置。内置的len函数将返回数组中元素的个数。
    • 默认情况下,数组的每个元素都被初始化为元素类型对应的零值。我们也可以使用数组字面值语法用一组值来初始化数组:
    var q [3]int = [3]int{1, 2, 3}
    var r [3]int = [3]int{1, 2}
    fmt.Println(r[2]) // "0"
    
    • 在数组字面值中,如果在数组的长度位置出现的是“...”省略号,则表示数组的长度是根据初始化值的个数来计算。
    q := [...]int{1, 2, 3}
    
    • 数组的长度是数组类型的一个组成部分,因此[3]int和[4]int是两种不同的数组类型。数组的长度必须是常量表达式,因为数组的长度需要在编译阶段确定。
    • 如果数组类型相同,数组元素类型相同,可以用"=="来比较。

    slice

    Slice(切片)代表变长的序列,序列中每个元素都有相同的类型。一个slice类型一般写作[]T,其中T代表slice中元素的类型;slice的语法和数组很像,只是没有固定长度而已。slice的底层引用一个数组对象。

    • 一个slice由三个部分构成:指针、长度和容量。
    • 指针指向第一个slice元素对应的底层数组元素的地址。
    • 长度对应slice中元素的数目;长度不能超过容量,容量一般是从slice的开始位置到底层数据的结尾位置。内置的len和cap函数分别返回slice的长度和容量。
    • slice的切片操作s[i:j],其中0 ≤ i≤ j≤ cap(s),用于创建一个新的slice,引用s的从第i个元素开始到第j-1个元素的子序列。新的slice将只有j-i个元素。
    • slice和数组的字面值语法很类似,它们都是用花括弧包含一系列的初始化元素,但是对于slice并没有指明序列的长度。这会隐式地创建一个合适大小的数组,然后slice的指针指向底层的数组。
    s := []int{0, 1, 2, 3, 4, 5}
    
    • 复制一个slice只是对底层的数组创建了一个新的slice别名。
    • 和数组不同的是,slice之间不能比较,因此我们不能使用==操作符来判断两个slice是否含有全部相等元素。slice唯一合法的比较操作是和nil比较,一个零值的slice等于nil。一个nil值的slice并没有底层数组。一个nil值的slice的长度和容量都是0。
    if s== nil { /* ... */ }
    
    • 内置的append函数用于向slice追加元素。每次调用append函数,必须先检测slice底层数组是否有足够的容量来保存新添加的元素。如果有足够空间的话,直接扩展slice,将新添加的元素复制到新扩展的空间,并返回slice。此时底层数组没有改变。如果没有足够的增长空间,append会进行内存扩展,分配一个足够大的sliace空间存放原来的内容,再把新增的内容添加进去。我们不能确认在原先的slice上的操作是否会影响到新的slice,因此,通常是将append返回的结果直接赋值给输入的slice变量:
    runes = append(runes, r)
    

    map

    参照python的字典类型,map是一个无序的key/value对的集合,其中所有的key都是不同的,然后通过给定的key可以在常数时间复杂度内检索、更新或删除对应的value。map类型可以写为map[K]V,其中K和V分别对应key和value。

    • map中所有的key都有相同的类型,所有的value也有着相同的类型,但是key和value之间可以是不同的数据类型。其中K对应的key必须是支持==比较运算符的数据类型,所以map可以通过测试key是否相等来判断是否已经存在。
    • 常用操作,创建,存储,访问,查找,删除
      创建可以使用内置函数make来创建,也可以使用map字面值语法来创建。访问方式通过key对应的下标语法访问。删除操作使用内置函数delete,delete会查找该元素,如果查找失败将返回value类型对应的零值,所以这种操作是安全的,即使map中不存在要删除的元素也没关系。
    ages := make(map[string]int) // mapping from strings to ints
    
    ages := map[string]int{
        "alice":   31,
        "charlie": 34,
    }
    
    ages["alice"] = 32
    fmt.Println(ages["alice"]) // "32"
    
    delete(ages, "alice") // remove element ages["alice"]
    
    • 和slice一样,map之间也不能进行相等比较;唯一的例外是和nil进行比较。如果我们需要对map进行比较操作,需要自己通过循环进行实现。

    结构体

    参见C语言的结构体,结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员。例:

    type Employee struct {
        ID        int
        Name      string
        Address   string
        DoB       time.Time
        Position  string
        Salary    int
        ManagerID int
    }
    
    var dilbert Employee
    

    dilbert结构体变量的成员可以通过点操作符访问,比如dilbert.Name和dilbert.DoB。
    通常一行对应一个结构体成员,成员的名字在前类型在后,不过如果相邻的成员类型如果相同的话可以被合并到一行,就像下面的Name和Address成员那样:

    type Employee struct {
        ID            int
        Name, Address string
        DoB           time.Time
        Position      string
        Salary        int
        ManagerID     int
    }
    
    • 结构体类型的零值是每个成员都对是零值。
    • 如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的,那样的话两个结构体将可以使用==或!=运算符进行比较。

    相关文章

      网友评论

          本文标题:go 基础

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