溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Go語言1

發(fā)布時(shí)間:2020-06-15 19:17:08 來源:網(wǎng)絡(luò) 閱讀:1648 作者:騎士救兵 欄目:編程語言

開發(fā)環(huán)境搭建

隨便學(xué)學(xué),平時(shí)用的都是WIndows10的操作系統(tǒng),就在這下面搞了。

下載安裝

下載go安裝程序,下載地址:https://golang.org/dl/ (被墻了,打不開)
墻內(nèi)下載地址http://www.golangtc.com/download
我下的是這個(gè):go1.9.2.windows-amd64.msi
安裝就下一步就好了,裝完之后驗(yàn)證一下。默認(rèn)已經(jīng)把Go的路徑加到環(huán)境變量中去了,所以可以直接在cmd中輸入go命令執(zhí)行:
Go語言1

環(huán)境變量

裝完之后,我裝到了D:\Go\,已經(jīng)幫我們添加好了2個(gè)環(huán)境變量:

  • GOROOT: D:\Go\
  • Path: D:\Go\bin
  • GOPATH: 這個(gè)環(huán)境變量也要,但是需要手動(dòng)設(shè)置

GOPATH也是要的,設(shè)置工作區(qū)路徑,不過如果沒有設(shè)置的話,有一個(gè)默認(rèn)位置(差不多就是系統(tǒng)的家目錄)。執(zhí)行幫助命令 go help gopath ,我這截了一段說明:

If the environment variable is unset, GOPATH defaults
to a subdirectory named "go" in the user's home directory
($HOME/go on Unix, %USERPROFILE%\go on Windows),
unless that directory holds a Go distribution.
Run "go env GOPATH" to see the current GOPATH.

暫時(shí)不設(shè)置也沒影響,反正也有默認(rèn)的,后面寫 Hellow World 的時(shí)候再看設(shè)置在哪個(gè)目錄

開發(fā)工具

開發(fā)工具先沒有裝,不過這里先記一下工具的名稱:

Visual Studio Code (簡(jiǎn)稱 VS Code / VSC) 是一款免費(fèi)開源的現(xiàn)代化輕量級(jí)代碼編輯器,支持幾乎所有主流的開發(fā)語言的語法高亮、智能代碼補(bǔ)全、自定義熱鍵、括號(hào)匹配、代碼片段、代碼對(duì)比 Diff、GIT 等特性,支持插件擴(kuò)展,并針對(duì)網(wǎng)頁(yè)開發(fā)和云端應(yīng)用開發(fā)做了優(yōu)化。軟件跨平臺(tái)支持 Win、Mac 以及 Linux。

這是一個(gè)通用的代碼編輯器,裝完之后要再裝一下go的擴(kuò)展:

  1. 點(diǎn)開,菜單-查看>>擴(kuò)展
  2. 搜索“go”,找到“Rich Go language support for Visual Studio Code”,作者“l(fā)ukehoban”,安裝
  3. 裝好之后用的時(shí)候

調(diào)試工具

Delve是一個(gè)非常棒的golang調(diào)試工具,支持多種調(diào)試方式,直接運(yùn)行調(diào)試,或者attach到一個(gè)正在運(yùn)行中的golang程序,進(jìn)行調(diào)試。
安裝Delve非常簡(jiǎn)單,直接運(yùn)行 go get 即可:

go get -u github.com/derekparker/delve/cmd/dlv

先記一下,我并沒有裝

寫代碼

在第一次寫代碼前,還要先把go的工作區(qū)設(shè)置好。

設(shè)置工作區(qū)

工作區(qū)是存放go源碼文件的目錄。一般情況,Go源碼文件都需要存放到工作區(qū)中,不過命令源碼文檔不是必須的。
每個(gè)工作區(qū)的目錄結(jié)構(gòu)都類似,有3個(gè)文件夾:

  • src/: 用于存放源碼以代碼包為組織形式
  • pkg/: 用于存放歸檔文件(名稱以.a為后綴的文件)
  • bin/: 用于存放當(dāng)前工作區(qū)中的GO程序的可執(zhí)行文件

找一個(gè)合適的位置,作為工作區(qū)。比如:E:\Go。這時(shí)就可以去把GOPATH環(huán)境變量設(shè)置為 “E:\Go” 。
按上面說的,還要工作區(qū)中還要?jiǎng)?chuàng)建3個(gè)文件夾,暫時(shí)只需要src,另外兩個(gè)暫時(shí)不用。

Hello World

到這里需要一些儀式感,就是寫一個(gè) Hellow World 。在src目錄下創(chuàng)建文件 “hellow.go” 。如果用了VS Code,會(huì)提示你裝一些擴(kuò)展,點(diǎn)下Install All,這樣會(huì)自動(dòng)把一些go的插件給裝好。
下面是程序的源碼:

package main

import(
    "fmt"
)

func main() {
    fmt.Println("Hello World")
}

第一部分,聲明這是一個(gè)包??蓤?zhí)行文件的包名必須是“main”。
第二部分,導(dǎo)入包。所有的庫(kù),包括自己的庫(kù),用之前都需要導(dǎo)入。fmt是包名,里面有一個(gè)方法可以向終端打印信息。
第三部分,定義了一個(gè)函數(shù)。必須要定義一個(gè)main函數(shù),這是個(gè)入口函數(shù)。
最后在cmd命令行里運(yùn)行一下:

go run hello.go

做加法

這次寫一個(gè)有參數(shù)有返回值的函數(shù):

package main

import(
    "fmt"
)

func add(a int, b int) int {
    var sum int
    sum = a + b
    return sum
}

func main(){
    var c int
    c = add(11, 22)
    fmt.Println("11 + 22 =", c)
}

go是一個(gè)強(qiáng)類型語言,所有變量都要設(shè)置類型。
add函數(shù),寫參數(shù)的時(shí)候,參數(shù)后面要用類型。并且由于add函數(shù)有返回值,返回值的類型也要在函數(shù)名后面定義好。
聲明變量用var,后面是變量名,在后面是變量的類型。
最后如果你的變量定義之后,沒有被應(yīng)用,也是會(huì)報(bào)錯(cuò)的。這樣強(qiáng)制你去掉冗余的代碼,這個(gè)變量既然沒有用到,自然就不需要出現(xiàn),比如把最后一句fmt.Println(c)去掉或者換成打印一個(gè)固定的字符串就會(huì)報(bào)錯(cuò)。

golang語言特性

  1. 垃圾回收
    • 內(nèi)存自動(dòng)回收,不需要開發(fā)人員管理內(nèi)存
    • 開發(fā)人員專注業(yè)務(wù)實(shí)現(xiàn),減輕負(fù)擔(dān)
    • 只需要new分配內(nèi)存,不需要釋放
  2. 天然并發(fā)
    • 從語言層面支持并發(fā),非常簡(jiǎn)單
    • goroute,輕量級(jí)線程,創(chuàng)建成千上萬個(gè)goroute成為可能
    • 基于CSP(Communicating Sequential Process)模型實(shí)現(xiàn)
  3. channel
    • 管道,類似Linux中的pipe
    • 多個(gè)goroute之間通過channel進(jìn)行通信
    • 支持任何類型
  4. 多返回值
    • 一個(gè)函數(shù)返回多個(gè)值

并發(fā)代碼演示

先寫一個(gè)程序goroute_print.go,很簡(jiǎn)單,就是傳入?yún)?shù)稍微計(jì)算一下,然后打印結(jié)果:

package main

import (
    "fmt"
)

func goroute_print(a int) {
    b := a + 10000
    fmt.Println(b)
}

這里用了 “:=” ,是聲明并賦值,并且系統(tǒng)自動(dòng)推斷類型,不需要var關(guān)鍵字。
然后再寫一個(gè)程序goroute_for.go,里面是一個(gè)for循環(huán),起若干個(gè)線程執(zhí)行:

package main

import(
    "time"
)

func main(){
    for i := 0; i < 1000; i++ {
        go goroute_print(i)
    }

    // sleep1秒,保證上面的子線程運(yùn)行結(jié)束
    time.Sleep(time.Second * 5)
}

在這個(gè)主程序要里調(diào)用另外一個(gè)文件里的程序。不過這2個(gè)文件都是同一個(gè)包,main包,開頭package聲明的。這里在前面加上了go調(diào)用,就是起了一個(gè)goroute。(不加go調(diào)用,就是在主線程里調(diào)用函數(shù),如果調(diào)用前加上go,就是起了一個(gè)子線程調(diào)用這個(gè)函數(shù))
主線程必須要等待子線程運(yùn)行結(jié)束才能退出,否則主線程一旦結(jié)束,其他子線程也就沒有了。這里簡(jiǎn)單粗暴的先用sleep來進(jìn)行等待。導(dǎo)入了一個(gè)time包。參數(shù) time.Second 就是1秒,如果不是等1秒而是幾秒的話,就乘以幾。
這里還有注釋,注釋的用法js一樣。單行注釋以 // 開頭,多行注釋以 /* 開始,以 */ 結(jié)尾。

執(zhí)行的話在cmd命令行執(zhí)行下面的命令:

go run goroute_for.go goroute_print.go

這個(gè)例子分在兩個(gè)文件里了,所以run后面把兩個(gè)文件都要寫上。順序是無所謂的。

管道代碼示例

這里用了make()函數(shù),是用來分配內(nèi)存的。第一個(gè)參數(shù) chan int ,表示要?jiǎng)?chuàng)建的是chan(管道),類型是int:

package main

import(
    "fmt"
)

func main(){
    pipe := make(chan int, 3)  // 創(chuàng)建一個(gè)管道,存的數(shù)據(jù)類型是int,容量是3
    pipe <- 11  // 往管道里存一個(gè)數(shù)
    fmt.Println(11, len(pipe))  // 打印長(zhǎng)度
    pipe <- 22
    fmt.Println(22, len(pipe))

    var t1 int
    t1 =<- pipe  // 從管道里取出一個(gè)數(shù),賦值給t1
    fmt.Println(t1, len(pipe))
    t1 =<- pipe
    fmt.Println(t1, len(pipe))
}

似乎就是個(gè)隊(duì)列,先進(jìn)先出,默認(rèn)滿了也會(huì)阻塞。之所以叫管道不叫隊(duì)列,可能是它能支持多個(gè)goroute之間通過channel(管道)進(jìn)行通信。暫時(shí)不需要這么深入。

通過管道在goroute之間傳遞參數(shù)

有兩種方式可以實(shí)現(xiàn)。把管道定義為全局變量,或者把管道作為函數(shù)的參數(shù)傳遞。代碼中使用全局變量不是好的做法,所以推薦用傳參的方法。不過兩種實(shí)現(xiàn)方法都看一下。
全局變量的代碼示例:

package main

import(
    "fmt"
)

var pipe chan int  // 在全局聲明pipe變量,下面兩個(gè)方法里都會(huì)用到

func add(a int, b int){
    sum := a + b
    pipe <- sum
}

func main(){
    pipe = make(chan int, 1)
    go add(1, 2)
    sum :=<- pipe
    fmt.Println("sum =", sum)
}

傳遞參數(shù)的代碼示例:

package main

import(
    "fmt"
)

func add(a int, b int, c chan int){
    sum := a + b
    c <- sum
}

func main(){
    var pipe chan int  // 在main函數(shù)里聲明pipe
    pipe = make(chan int, 1)
    go add(3, 4, pipe)
    sum :=<- pipe
    fmt.Println("sum =", sum)
}

多返回值的代碼示例

package main

import(
    "fmt"
)

func calc(a int, b int)(int, int){
    c1 := a + b
    c2 := (a + b)/2
    return c1, c2
}

func main(){
    sum , avg := calc(100, 200)
    fmt.Println("sum =", sum, "avg =", avg)
}

如果多返回值里只想取其中部分的值,可以把剩下的值傳給占位符(“_”):

package main

import(
    "fmt"
)

func calc(a int, b int)(int, int){
    c1 := a + b
    c2 := (a + b)/2
    return c1, c2
}

func main(){
    sum , _ := calc(100, 200)
    fmt.Println("sum =", sum)
    fmt.Println(_)  // 打印下劃線會(huì)報(bào)錯(cuò)
}

嘗試打印下劃線的報(bào)錯(cuò)信息如下:

>go run calc.go
# command-line-arguments
.\calc.go:17:16: cannot use _ as value

代碼格式化(gofmt)

go默認(rèn)提供一個(gè)代碼格式化命令 gofmt ,會(huì)按照Go語言代碼規(guī)范格式化文件中的代碼。把之前的hello.go的代碼搞亂一點(diǎn),簡(jiǎn)單一點(diǎn)就改下縮進(jìn)好了,然后運(yùn)行下面的命令:

gofmt hello.go

最終還是能再命令行里看到整潔的代碼
Go語言1
還截圖的效果,縮進(jìn)是8個(gè)字符的位置。
不過原文件里還是亂糟糟的,可以加上-w參數(shù),改寫原文件:

gofmt -w hello.go

實(shí)際寫到文件里,縮進(jìn)用的不是空格是Tab,具體效果是幾個(gè)字符就看文件的設(shè)置了。
另外換行是 \n ,這個(gè)windows的記事本下會(huì)用問題,記事本的換行是 \r\n 。

編譯Go源文件

創(chuàng)建Go工作目錄:H:\Go\src\go_dev\day1\hello\。把之前寫的示例hello.go放到hello目錄下。再建個(gè)文件夾,其他示例的源文件放到examples目錄下。這里src文件夾的名字是不能變的,其他文件夾的名字包括上級(jí)的都隨意。
上面的演示都沒有對(duì)go的原文件進(jìn)行編譯,編譯要用 go build 命令:

H:\Go>go build go_dev\day1\hello
can't load package: package go_dev/day1/hello: cannot find package "go_dev/day1/hello" in any of:
        D:\Go\src\go_dev\day1\hello (from $GOROOT)
        C:\Users\Steed\go\src\go_dev\day1\hello (from $GOPATH)

H:\Go>

這里并沒有編譯成功,原因是找不到我們的源文件。之前因?yàn)闆]設(shè)置環(huán)境變量GOPATH,所以默認(rèn)沒有設(shè)置GOPATH就是把用戶家目錄作為GOPATH。這個(gè)GOPATH就是工作目錄。
先去把GOPATH的環(huán)境變量設(shè)置好,我這里 “H:\Go\“ 就是我的工作目錄,設(shè)置成:“H:\Go\“ 。建議設(shè)置用戶變量,當(dāng)然系統(tǒng)變量也沒問題。
完成之后要新開一個(gè)cmd,也有可能有要注銷當(dāng)前用戶,下次登錄才能完全生效。然后可以驗(yàn)證一下:

C:\Users\Steed>go env GOPATH
H:\Go\

然后再編譯一下,這次隨便找個(gè)目錄執(zhí)行命令:

J:\tmp>go build go_dev\day1\hello

J:\tmp>hello.exe
Hello World

命令執(zhí)行后會(huì)在當(dāng)前目錄下生成hello.exe的可執(zhí)行文件,上面也直接執(zhí)行了一下。文件名以及生成文件的路徑都是可以用參數(shù)指定的。
前面只所以不把所有的示例都放一起,是因?yàn)槠渌纠灿衜ain入口函數(shù),存在多個(gè)入口函數(shù)編譯會(huì)報(bào)錯(cuò)。
其實(shí)可以編譯單個(gè)文件,但是路徑要寫全(絕對(duì)路徑或相對(duì)路徑),單文件編譯似乎不受環(huán)境變量影響:

J:\tmp>go build H:\Go\src\go_dev\day1\hello\hello.go

復(fù)雜一點(diǎn)的程序一般都不是單個(gè)文件的,所以一個(gè)文件夾里就是一個(gè)小項(xiàng)目,只有一個(gè)入口函數(shù)。這樣編譯不會(huì)有問題。另外如果使用開發(fā)工具的話,這種情況開發(fā)工具直接也會(huì)提示錯(cuò)誤的。
最后,下面這么搞比較方便:

H:\Go\src>go build go_dev\day1\hello

H:\Go\src>go build go_dev\day1\hello\hello.go

選好cmd的啟動(dòng)路徑,之后源文件的路徑用Tab就能自動(dòng)補(bǔ)全出來了。

包的概念

代碼不能單獨(dú)存在,任何代碼都屬于一個(gè)包。所以第一行代碼,一定寫的是package。

  1. 把相同功能的代碼放到一個(gè)目錄,稱之為包。
  2. 包可以被其他包調(diào)用。
  3. main包是用來生成可執(zhí)行文件的,沒個(gè)程序只有一個(gè)main包
  4. 包的主要用途是提高代碼的可復(fù)用性。

使用多個(gè)包的代碼示例

新建個(gè)文件夾package_exampl,寫一個(gè)由多個(gè)包多個(gè)文件構(gòu)成的代碼,具體如下:

// go_dev/day1/package_example/calc/add.go
package calc

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

// go_dev/day1/package_example/calc/sub.go
package calc

func Sub(x int, y int) int {
    return x - y
}

// go_dev/day1/package_example/main/main.go
package main

import(
    // "calc"
    "go_dev/day1/package_example/calc"
    "fmt"
)

func main(){
    sum := calc.Add(100, 200)
    sub := calc.Sub(10, 5)
    fmt.Println(sum, sub)
}

calc包里的Add和Sub函數(shù)都是首字母大寫的。在Go語言里首字母大寫有特殊的意義,就是全局變量。如果都小寫的話,在main函數(shù)里這兩個(gè)函數(shù),由于作用域的問題,都是不可見的。
結(jié)論:就是說一個(gè)變量(包括函數(shù)),如果要被外部的包調(diào)用,必須要首字母大寫。不過變量不推薦這么做,因?yàn)檫@樣這個(gè)變量就是全局變量,而全局變量是不推薦使用的方法(別的語言里也同樣是不推薦使用全局變量的)。
main函數(shù)里導(dǎo)入包的時(shí)候,calc包的路徑要寫全,不能只寫 “calc” ,下面會(huì)具體分析。
編譯過程
先把import改成這樣,一步一步排錯(cuò):

import(
    "calc"
    // "go_dev/day1/package_example/calc"
    "fmt"
)

多個(gè)包的編譯

H:\Go\src>go build go_dev\day1\package_example
can't load package: package go_dev/day1/package_example: no Go files in H:\Go\src\go_dev\day1\package_example

這里報(bào)錯(cuò),因?yàn)槲募A下確實(shí)沒有包,包的源文件在下一級(jí)的目錄里:

H:\Go\src>go build go_dev\day1\package_example\main
go_dev\day1\package_example\main\main.go:4:5: cannot find package "calc" in any of:
        D:\Go\src\calc (from $GOROOT)
        H:\Go\src\calc (from $GOPATH)

繼續(xù)報(bào)錯(cuò),這是找不到calc包,并且這里也看到了默認(rèn)查找包的路徑。現(xiàn)在包的路徑應(yīng)該怎么寫全就清除了,把import的內(nèi)容改回來,之后就能順利的編譯執(zhí)行了:

H:\Go\src>go build go_dev\day1\package_example\main

H:\Go\src>main.exe
300 5

指定路徑和文件名
一般編譯后生成的可執(zhí)行文件是放在工作目錄下的bin目錄下的。還是用go build命令,加上-o參數(shù),windows系統(tǒng)下得加上擴(kuò)展名exe之后才能執(zhí)行:

H:\Go\src>go build -o ../bin/test.exe go_dev\day1\package_example\main

H:\Go\src>cd ../bin

H:\Go\bin>test
300 5

H:\Go\bin>

線程和管道的代碼示例

程序分成2部分。一部分是業(yè)務(wù)邏輯,放在goroute目錄下。還有一部分是函數(shù)的入口,放在main目錄下。文件結(jié)構(gòu):

H:\GO\SRC\GO_DEV\DAY1\GOROUTE_EXAMPLE
├─goroute
└─main

業(yè)務(wù)邏輯就是,獲取3個(gè)變量,2個(gè)數(shù)和1個(gè)管道,把2個(gè)數(shù)的和計(jì)算后,存到管道里。
主函數(shù)里則是,單獨(dú)起一個(gè)線程goroute進(jìn)行計(jì)算,之后從管道里獲取到計(jì)算結(jié)果,打印出來。
這個(gè)例子里用到了線程goroute,以及管道用于goroute之間的通信。之前已經(jīng)演示過了,這里就是把代碼分成2個(gè)包再寫一遍:

// goroute/add.go
package goroute

func Add(a int, b int, c chan int){
    sum := a + b
    c <- sum
}

// goroute/main.go
package main

import(
    "go_dev/day1/goroute_example/goroute"
    "fmt"
)

func main(){
    var pipe chan int
    pipe = make(chan int, 1)

    go goroute.Add(100, 200, pipe)
    res := <- pipe
    fmt.Println(res)    
}

官網(wǎng) Go 指南

都是中文版的。
Go 語言之旅:https://tour.go-zh.org/welcome/1
Go 編程語言:https://go-zh.org/
Go 語言之旅可以作為入門教程,并且可以Web在線寫代碼,提交執(zhí)行在Web上顯示結(jié)果。

課后作業(yè)

使用fmt分別打印字符串、二進(jìn)制、十進(jìn)制、十六進(jìn)制、浮點(diǎn)數(shù)。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

go
AI