golang快速入门

栏目: Go · 发布时间: 6年前

内容简介:Go(又称罗伯特·格瑞史莫(Robert Griesemer),罗勃·派克(Rob Pike)及Robert Griesemer, Rob Pike 和 Ken Thompson。Robert在开发Go之前是Google V8、Chubby和HotSpot JVM的主要贡献者;Rob主要是Unix、UTF-8、plan9的作者;Ken主要是B语言、C语言的作者、Unix之父。

语言介绍

Go(又称 Golang )是 Google 开发的一种 静态 强类型 、编译型、并发型,并具有垃圾回收功能的 编程语言

罗伯特·格瑞史莫(Robert Griesemer),罗勃·派克(Rob Pike)及 肯·汤普逊 (Ken Thompson)于2007年9月开始设计Go,稍后Ian Lance Taylor、Russ Cox加入项目。Go是基于 Inferno 操作系统所开发的。Go于2009年11月正式宣布推出,成为开放源代码项目,并在 LinuxMac OS X 平台上进行了实现,后来追加了Windows系统下的实现。在2016年,Go被软件评价公司TIOBE 选为“TIOBE 2016 年最佳语言”

Robert Griesemer, Rob Pike 和 Ken Thompson。Robert在开发 Go 之前是Google V8、Chubby和HotSpot JVM的主要贡献者;Rob主要是Unix、UTF-8、plan9的作者;Ken主要是B语言、 C语言 的作者、Unix之父。

为什么会设计go语言

设计Go语言是为了解决当时Google开发遇到的以下这些问题:

大量的C++代码,同时又引入了 Java Python

成千上万的工程师

数以万计行的代码

分布式的编译系统

数百万的服务器

其主要有以下几个方面的痛点:

编译慢

失控的依赖

每个工程师只是用了一个语言里面的一部分

程序难以维护(可读性差、文档不清晰等)

更新的花费越来越长

交叉编译困难

所以,他们当时设计Go的目标是为了消除各种缓慢和笨重、改进各种低效和扩展性。Go是由那些开发大型系统的人设计的,同时也是为了这些人服务的;它是为了解决工程上的问题,不是为了研究语言设计;它还是为了让我们的编程变得更舒适和方便

语法规则介绍

包管理

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    fmt.Println("My favorite number is", rand.Intn(10))
}

output:
My favorite number is 1

每个 Go 程序都是由包组成的。

程序运行的入口是包 main

这个程序使用并导入了包 "fmt" 和 "math/rand"

按照惯例,包名与导入路径的最后一个目录一致。

例如, "math/rand" 包由 package rand 语句开始。

同一目录下只能用同一个包名

  • 导出名

在导入了一个包之后,就可以用其导出的名称来调用它。

在 Go 中,首字母大写的名称是被导出的。

Foo 和 FOO 都是被导出的名称。名称 foo 是不会被导出的。

foo-相当于 php 的private , Foo 和 FOO 相当于public

package exports

import "fmt"

func Foo(){
    fmt.Println("it is Foo")
}

func FOO(){
    fmt.Println("it is FOO")
    foo()
}

func foo(){
    fmt.Println("it is foo")
}

函数

package main

import "fmt"

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

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

func add3(x, y int) (z int){
    z = x+y
    return
}

func main() {
    fmt.Println(add(42, 13))
    fmt.Println(add2(42, 13))
    fmt.Println(add3(42, 13))
}

output:
55
55
55

函数格式 func 函数名(无参/参数1, 参数2...)(返回结果|无){ 函数体}

执行发现这三个函数返回结果相同,只是格式不同

add-函数符合基本的函数格式

add2-对于同一类型参数,可以在最后一个参数后面指明参数类型

add3-函数返回结果可以在返回结构中声明

  • 多值返回
package main

import "fmt"

func swap(x, y string) (string, string) {
    return y, x
}

func main() {
    a, b := swap("hello", "world")
    fmt.Println(a, b)
}

output:
world hello

函数可以返回任意数量的返回值。

swap 函数返回了两个字符串。

变量

package main

import "fmt"

var (
    a  bool
    b  string
    c  int        //有符号-等于cpu位数-- 如果是64位(-9223372036854775808 到 9223372036854775807)-`uname -m`
    c1 int8       //有符号-占用8bit(-128 到 127),int16,int32,int64类推
    d  uint       //无符号-等于cpu位数- 如果是64位(0 到 18446744073709551615)
    d1 uint8      //无符号-占用8bit(0 到 255), uint16,uint32,uint64类推
    e  rune       // int32 别名
    f  byte       // uint8 别名
    g  float32    //1.401298464324817070923729583289916131280e-45 -- 3.402823466385288598117041834516925440e+38 (23位小数f,8位偏置指数e,1位符号s)
    h  float64    //4.940656458412465441765687928682213723651e-324 -- 1.797693134862315708145274237317043567981e+308(52位小数f,11位偏置指数e,1位符号s)
    j  complex64  //32 位实数和虚数
    k  complex128 //64 位实数和虚数
)

var aa, bb, ee bool
var ff, gg int = 5, 6
var hh, ii = 5, true

func main() {
    var aaa, bbb bool = true, false
    ccc, ddd := true, 18
    fmt.Println(a, b, c, c1, d, d1, e, f, g, h, j, k)
    fmt.Println(aa, bb, ee)
    fmt.Println(ff, gg)
    fmt.Println(hh, ii)
    fmt.Println(aaa, bbb)
    fmt.Println(ccc, ddd)
}
output  
false  0 0 0 0 0 0 0 0 (0+0i) (0+0i)
false false false
5 6
5 true
true false
true 18

go 基本的数据类型有bool类型,字符串类型,数字类型(复数类型)

默认值分别为 false, "", 0(0+0i)

变量定义可以包含初始值,每个变量对应一个。

如果省略类型;变量从初始值中获得类型

在函数中, := 简洁赋值语句在明确类型的地方,可以用于替代 var 定义。

函数外的每个语句都必须以关键字开始( varfunc 、等等), := 结构不能使用在函数外

  • 数字类型转换
package main

import (
    "fmt"
    "math"
    "strconv"
)

//StringToInt 字符串转整形
func StringToInt(valstr string) int {
    val, err := strconv.Atoi(valstr)
    if err != nil {
        val = 0
    }
    return val
}

//IntToString 整形转字符串
func IntToString(intval int) string {
    return strconv.Itoa(intval)
}

func main() {
    var x, y int = 3, 4
    var f float64 = math.Sqrt(float64(x*x + y*y))
    var z int = int(f)
    fmt.Println(x, y, z)

    a := IntToString(x) + "string"
    b := "168"
    c := StringToInt(b)
    fmt.Println(a, b+"s", c)
}

output:
3 4 5
3string 168s 168

表达式 T(v) 将值 v 转换为类型 T
数字类型一般可以直接显式转化,字符串和数字类型可以借助strconv包处理

  • 类型推导
package main

import "fmt"

func main() {
    i := 42           // int
    f := 3.142        // float64
    g := 0.867 + 0.5i // complex128
    var j bool
    k := j
    fmt.Printf("The types is %T, %T, %T, %T\n", i, f, g, k)
}
output:
The types is int, float64, complex128, bool

在定义一个变量但不指定其类型时(使用没有类型的 var 或 := 语句), 变量的类型由右值推导得出。当右值定义了类型时,新变量的类型与其相同

常量

package main

import "fmt"

const Pi = 3.14

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
}

func main() {
    const World = "世界"
    fmt.Println("Hello", World)
    fmt.Println("Happy", Pi, "Day")

    const Truth = true
    fmt.Println("Go rules?", Truth)

    fmt.Println(needInt(Small))
    fmt.Println(needFloat(Small))
    fmt.Println(needFloat(Big))
}
output:
Hello 世界
Happy 3.14 Day
Go rules? true
21
0.2
1.2676506002282295e+29

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

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

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

一个未指定类型的数值常量可以作为不同数字类型传参

循环

package main

import "fmt"

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

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

output:
45
1024

Go 只有一种循环结构—— for 循环。

基本的 for 循环除了没有了 ( ) 之外(甚至强制不能使用它们),看起来跟 C 或者 Java 中做的一样,而 { } 是必须的。

  • 死循环
package main

import "fmt"

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

output:
hello world
hello world
....

不手动停止的话,代码会一直运行下去

if 语句

package main

import (
    "fmt"
    "math"
)

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

func main() {
    fmt.Println(sqrt(2), sqrt(-4))
}
output:
1.4142135623730951 2i

if 语句除了没有了 ( ) 之外(甚至强制不能使用它们),看起来跟 C 或者 Java 中的一样,而 { } 是必须的。

  • if 的便捷语句
package main

import (
    "fmt"
    "math"
)

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

func pow2(x, n, lim float64) float64 {
    if v := math.Pow(x, n); v < lim {
        return v
    } else {
        fmt.Printf("%g >= %g\n", v, lim)
    }
    // 这里开始就不能使用 v 了
    return lim
}

func main() {
    fmt.Println(
        pow(3, 2, 10),
        pow(3, 3, 20),
        pow2(3, 2, 10),
        pow2(3, 3, 20),
    )
}
output:
27 >= 20
9 20 9 20

跟 for 一样, if 语句可以在条件之前执行一个简单的语句。

由这个语句定义的变量的作用域仅在 if 范围之内。

在 if 的便捷语句定义的变量同样可以在任何对应的 else 块中使用。

switch 语句

package main

import (
    "fmt"
    "runtime"
)

func main() {
    fmt.Print("Go runs on ")
    switch os := runtime.GOOS; os {
    case "darwin":
        fmt.Println("OS X.")
    case "linux":
        fmt.Println("Linux.")
    default:
        // freebsd, openbsd,
        // plan9, windows...
        fmt.Printf("%s.", os)
    }
}
output:
Go runs on OS X

switch 结构同其他语言一样

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

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

  • 没有条件的 switch
package main

import (
    "fmt"
    "time"
)

func main() {
    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.")
    }
}
output:
Good evening.

没有条件的 switch 同 switch true 一样。

这一构造使得可以用更清晰的形式来编写长的 if-then-else 链。

defer 语句

package main

import "fmt"

func echo(str string) string {
    defer fmt.Println("echo end")
    return "hello " + str
}
func main() {
    defer fmt.Println("world")

    fmt.Println("hello")

    echo("china")
}
output:
hello
echo end
world

defer 语句会延迟函数的执行直到上层函数返回。

延迟调用的参数会立刻生成,但是在上层函数返回前函数都不会被调用

  • defer 栈
package main

import "fmt"

func main() {
    fmt.Println("counting")

    for i := 0; i < 3; i++ {
        defer fmt.Println(i)
    }

    fmt.Println("done")
}
output:
counting
done
2
1
0

延迟的函数调用被压入一个栈中。当函数返回时, 会按照后进先出的顺序调用被延迟的函数调用。

派生类型

指针

package main

import "fmt"

func main() {
    i, j := 42, 2701

    p := &i         // point to i
    fmt.Println(*p) // read i through the pointer
    *p = 21         // set i through the pointer
    fmt.Println(i)  // see the new value of i

    p = &j         // point to j
    *p = *p / 37   // divide j through the pointer
    fmt.Println(j) // see the new value of j
}
output:
42
21
73

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

类型 *T 是指向类型 T 的值的指针。其零值是 nil

var p

int

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

i := 42

p = &i

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

p) // 通过指针 p 读取 i

*p = 21 // 通过指针 p 设置 i

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

结构体

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    fmt.Println(v)
    v.X = 4
    fmt.Println(v)
    p := &v
    p.X = 1.5e9
    fmt.Println(v, v.X)
    fmt.Println(p, p.X)
    fmt.Println("------------------------")
    v1 := Vertex{1, 2}  // 类型为 Vertex
    v2 := Vertex{X: 1}  // Y:0 被省略
    v3 := Vertex{}      // X:0 和 Y:0
    p1 := &Vertex{1, 2} // 类型为 *Vertex
    fmt.Println(v1, v2, v3, p1)
    fmt.Printf("values is %+v, %+v, %+v, %+v \n", v1, v2, v3, p1)
    fmt.Printf("types is %T, %T, %T, %T \n", v1, v2, v3, p1)
    fmt.Printf("values is %#v, %#v, %#v, %#v \n", v1, v2, v3, p1)
}
output:
{1 2}
{4 2}
{1500000000 2} 1500000000
&{1500000000 2} 1500000000
------------------------
{1 2} {1 0} {0 0} &{1 2}
values is {X:1 Y:2}, {X:1 Y:0}, {X:0 Y:0}, &{X:1 Y:2} 
types is main.Vertex, main.Vertex, main.Vertex, *main.Vertex 
values is main.Vertex{X:1, Y:2}, main.Vertex{X:1, Y:0}, main.Vertex{X:0, Y:0}, &main.Vertex{X:1, Y:2}

一个结构体( struct )就是一个字段的集合。

结构体字段使用点号来访问.

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

数组

package main

import "fmt"

func main() {
    var a [2]string
    a[0] = "Hello"
    a[1] = "World"
    fmt.Println(a[0], a[1])
    fmt.Println(a)
}

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

表达式 var a [10]int

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

数组的长度是其类型的一部分,因此数组不能改变大小

这看起来是一个制约,但是请不要担心; Go 提供了更加便利的方式来使用数组。

slice

package main

import "fmt"

func main() {
    p := []int{2, 3, 5, 7, 11, 13}
    fmt.Println("p ==", p)

    for i := 0; i < len(p); i++ {
        fmt.Printf("p[%d] == %d\n", i, p[i])
    }
}
output:
p == [2 3 5 7 11 13]
p[0] == 2
p[1] == 3
p[2] == 5
p[3] == 7
p[4] == 11
p[5] == 13

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

[]T 是一个元素类型为 T 的 slice。

  • 对 slice 切片
package main

import "fmt"

func main() {
    p := []int{2, 3, 5, 7, 11, 13}
    fmt.Println("p ==", p)
    fmt.Println("p[1:4] ==", p[1:4])

    // 省略下标代表从 0 开始
    fmt.Println("p[:3] ==", p[:3])

    // 省略上标代表到 len(s) 结束
    fmt.Println("p[4:] ==", p[4:])
}

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

表达式 s[lo:hi] 表示从 lo 到 hi-1 的 slice 元素,含两端。

因此 s[lo:lo] 是空的,而 s[lo:lo+1] 有一个元素。

  • 构造 slice
package main

import "fmt"

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)
}
output:
a len=5 cap=5 [0 0 0 0 0]
b len=0 cap=5 []
c len=2 cap=5 [0 0]
d len=3 cap=3 [0 0 0]

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

  • nil slice
package main

import "fmt"

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

slice 的零值是 nil

一个 nil 的 slice 的长度和容量是 0。

  • 向 slice 添加元素
package main

import "fmt"

func main() {
    var a []int
    printSlice("a", a)

    // append works on nil slices.
    a = append(a, 0)
    printSlice("a", a)

    // the slice grows as needed.
    a = append(a, 1)
    printSlice("a", a)

    // we can add more than one element at a time.
    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)
}
output:
a len=0 cap=0 []
a len=1 cap=1 [0]
a len=2 cap=2 [0 1]
a len=5 cap=6 [0 1 2 3 4]

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

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

append 的第一个参数 s 是一个类型为 T 的数组,其余类型为 T 的值将会添加到 slice。

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

如果 s 的底层数组太小,而不能容纳所有值时,会分配一个更大的数组。

返回的 slice 会指向这个新分配的数组。

map

package main

import "fmt"

type Vertex struct {
    Lat, Long float64
}

var m map[string]Vertex

func main() {
    m = make(map[string]Vertex)
    m["Bell Labs"] = Vertex{
        40.68433, -74.39967,
    }
    fmt.Println(m["Bell Labs"])

    m := make(map[string]int)

    m["Answer"] = 42
    fmt.Println("The value:", m["Answer"])

    m["Answer"] = 48
    fmt.Println("The value:", m["Answer"])

    delete(m, "Answer")
    fmt.Println("The value:", m["Answer"])

    v, ok := m["Answer"]
    fmt.Println("The value:", v, "Present?", ok)
}
output:
{40.68433 -74.39967}
The value: 42
The value: 48
The value: 0
The value: 0 Present? false

map 映射键到值。

map 在使用之前必须用 make 而不是 new 来创建;值为 nil 的 map 是空的,并且不能赋值

在 map m 中插入或修改一个元素:

m[key] = elem

获得元素:

elem = m[key]

删除元素:

delete(m, key)

通过双赋值检测某个键存在:

elem, ok = m[key]

如果 key 在 m 中, ok 为 true 。否则, ok 为 false ,并且 elem 是 map 的元素类型的零值。

同样的,当从 map 中读取某个不存在的键时,结果是 map 的元素类型的零值。

range

package main

import (
    "fmt"
    "sort"
)

var pow = []int{1, 2, 4, 8}

func main() {
    for i, v := range pow {
        fmt.Printf("2**%d = %d\n", i, v)
    }

    m := make(map[string]int)
    m["Answer"] = 42
    m["welcome"] = 48

    var mp []string
    for k, v := range m {
        fmt.Printf("key:%s, value:%d \n", k, v)
        mp = append(mp, k)
    }

    sort.Strings(mp)
    for _, k := range mp {
        fmt.Printf("key:%s, value:%d \n", k, m[k])
    }
    pow := make([]int, 3)
    for i := range pow {
        pow[i] = 1 << uint(i)
    }
    for _, value := range pow {
        fmt.Printf("%d\n", value)
    }
}

output:
2**0 = 1
2**1 = 2
2**2 = 4
2**3 = 8
key:Answer, value:42 
key:welcome, value:48 
key:Answer, value:42 
key:welcome, value:48 
1
2
4

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

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

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


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

黑客与画家

黑客与画家

[美] Paul Graham / 阮一峰 / 人民邮电出版社 / 2013-10 / 69.00元

本书是硅谷创业之父Paul Graham 的文集,主要介绍黑客即优秀程序员的爱好和动机,讨论黑客成长、黑客对世界的贡献以及编程语言和黑客工作方法等所有对计算机时代感兴趣的人的一些话题。书中的内容不但有助于了解计算机编程的本质、互联网行业的规则,还会帮助读者了解我们这个时代,迫使读者独立思考。 本书适合所有程序员和互联网创业者,也适合一切对计算机行业感兴趣的读者。一起来看看 《黑客与画家》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具