主頁(yè) > 知識(shí)庫(kù) > Golang import本地包和導(dǎo)入問(wèn)題相關(guān)詳解

Golang import本地包和導(dǎo)入問(wèn)題相關(guān)詳解

熱門標(biāo)簽:呼叫中心市場(chǎng)需求 鐵路電話系統(tǒng) 服務(wù)外包 網(wǎng)站排名優(yōu)化 百度競(jìng)價(jià)排名 地方門戶網(wǎng)站 Linux服務(wù)器 AI電銷

1 本地包聲明

包是Go程序的基本單位,所以每個(gè)Go程序源代碼的開始都是一個(gè)包聲明:

package pkgName

這就是包聲明,pkgName 告訴編譯器,當(dāng)前文件屬于哪個(gè)包。一個(gè)包可以對(duì)應(yīng)多個(gè)*.go源文件,標(biāo)記它們屬于同一包的唯一依據(jù)就是這個(gè)package聲明,也就是說(shuō):無(wú)論多少個(gè)源文件,只要它們開頭的package包相同,那么它們就屬于同一個(gè)包,在編譯后就只會(huì)生成一個(gè).a文件,并且存放在$GOPATH/pkg文件夾下。

示例:

(1) 我們?cè)?GOPATH/目錄下,創(chuàng)建如下結(jié)構(gòu)的文件夾和文件:

分別寫入如下的代碼:

hello.go

//hello.go
package hello

import (
  "fmt"
)

func SayHello() {
  fmt.Println("SayHello()-->Hello")
}

hello2.go

//hello2.go
package hello

import (
  "fmt"
)

func SayWorld() {
  fmt.Println("SayWorld()-->World")
}

main.go

//main.go
package main

import (
  "hello"
)

func main() {
  hello.SayHello()
  hello.SayWorld()
}

分析:

根據(jù)hello.go/hello2.go中的package聲明可知,它們倆屬于同一個(gè)包–hello,那么根據(jù)分析,編譯后只會(huì)生成一個(gè)*.a文件。

執(zhí)行命令:

go install hello

該命令的意思大概是:編譯并安裝hello包,這里安裝的意思是將生成的*.a文件放到工作目錄$GOPATH/pkg目錄下去

運(yùn)行后:

從結(jié)果看出,果然只生成了一個(gè)包,并且名為hello.a

那么我們提出第二個(gè)問(wèn)題:生成的*.a文件名是否就是我們定義的包名+.a后綴?

為了驗(yàn)證這個(gè)問(wèn)題,我們對(duì)源碼做一些更改:
將hello.go/hello2.go中的package聲明改為如下:

package hello_a

在編譯安裝包之前,先清除上一次生成的包:

go clean -i hello

再次編譯安裝該包:

go install hello_a

按照“正常推理”,上面這句命令是沒(méi)什么問(wèn)題的,因?yàn)槲覀円呀?jīng)將包名改成hello_a了啊,但是實(shí)際的運(yùn)行結(jié)果是這樣的:

oh~No!!
那么,我們?cè)僭囋囉眠@條命令:

go install hello

臥槽?。【尤怀晒α耍。∈遣皇???

那么我們嘗試生成一下可執(zhí)行程序,看看能不能正常運(yùn)行呢?

go build main

又報(bào)錯(cuò)了?。。?/p>

看這報(bào)錯(cuò)提示,好像應(yīng)該改一下main.go源碼,那就改成如下吧:

//main.go
package main

import (
  "hello_a"
)

func main() {
  hello_a.SayHello()
  hello_a.SayWorld()
}

改成上面這樣也合情合理哈?畢竟我們把包名定義成了hello_a了!
那就再來(lái)編譯一次吧:

go build main

繼續(xù)報(bào)錯(cuò)!

等等??!有新的發(fā)現(xiàn),對(duì)比上兩次的報(bào)錯(cuò)信息,可見(jiàn)第一次還能能找到hello_a包的,更改源碼后居然還TM找不到hello_a包了??
好吧,那咱再改回去,不過(guò)這回只改包的導(dǎo)入語(yǔ)句,改成:

import (
  "hello"
)

再次編譯:

go build main

臥槽?。【尤粵](méi)報(bào)錯(cuò)了??!再運(yùn)行一下可執(zhí)行程序:

好吧,終于得到了想要的結(jié)果!

那進(jìn)行到這里能說(shuō)明什么呢?

(1) 一個(gè)包確實(shí)可以由多個(gè)源文件組成,只要它們開頭的包聲明一樣
(2)一個(gè)包對(duì)應(yīng)生成一個(gè)*.a文件,生成的文件名并不是包名+.a
(3) go install ××× 這里對(duì)應(yīng)的并不是包名,而是路徑名??!
(4) import ××× 這里使用的也不是包名,也是路徑名!
(5) ×××××.SayHello() 這里使用的才是包名!

那么問(wèn)題又來(lái)了,我們?cè)撊绾卫斫猓?)、(4)中的路徑名呢?
我覺(jué)得,可以這樣理解:
這里指定的是該×××路徑名就代表了此目錄下唯一的包,編譯器連接器默認(rèn)就會(huì)去生成或者使用它,而不需要我們手動(dòng)指明!

好吧,問(wèn)題又來(lái)了,如果一個(gè)目錄下有多個(gè)包可以嗎?如果可以,那該怎么編譯和使用??

那我們繼續(xù)改改源代碼:

首先,保持hello2.go 不變,改動(dòng)hello.go為如下代碼:

//hello.go
package hello

import (
  "fmt"
)

func SayHello() {
  fmt.Println("SayHello()-->Hello")
}

并且更改main.go的源碼如下

//main.go
package main

import (
  "hello"
)

func main() {
  hello.SayHello()
  hello_a.SayWorld()
}

再次清理掉上次生成的可執(zhí)行程序與包:

go clean -i hello
go clean -x main

你可以試著執(zhí)行如上的命令,如果不能清除,那就手動(dòng)刪除吧!
反正,還原成如下樣子:

那么再次嘗試編譯并安裝包,不過(guò)注意了,此時(shí)hello目錄下有兩個(gè)包了,不管是否正確,我們先嘗試一下:

go install hello

oh~~果然出錯(cuò)了!!

 
看到了嗎?它說(shuō)它找到了兩個(gè)包了?。。?!

那這能說(shuō)明什么呢??

其實(shí)這就更加確定的說(shuō)明了,我們上面的推測(cè)是正確的!

(3) go install ××× 這里對(duì)應(yīng)的并不是包名,而是路徑名??!

這里指定的是該×××路徑名就代表了此目錄下唯一的包,編譯器連接器默認(rèn)就會(huì)去生成或者使用它,而不需要我們手動(dòng)指明!

好吧,證明了這個(gè)還是挺興奮的!!那我們繼續(xù)!!

如果一個(gè)目錄下,真的有兩個(gè)或者更多個(gè)包,那該如何生成??
抱著試一試的態(tài)度,我嘗試了許多可能,但無(wú)一正確,最后一個(gè)命令的結(jié)果是讓我崩潰的:

go help install

恩!對(duì)!你沒(méi)有看錯(cuò):installs the packages named by the import paths
What the fuck!! 以后還是決定要先看文檔再自己做測(cè)試?。?/p>

好吧,綜上所述,一個(gè)目錄下就只能有一個(gè)包吧,因?yàn)槎际侵付窂?,沒(méi)有辦法指定路徑下的某個(gè)具體的包,這樣的做法其實(shí)也挺好,讓源代碼結(jié)構(gòu)更清晰!

2 包的導(dǎo)入問(wèn)題

導(dǎo)入包:

  • 標(biāo)準(zhǔn)包使用的是給定的短路徑,如"fmt"、"net/http"
  • 自己的包,需要在工作目錄(GOPATH)下指定一個(gè)目錄,improt 導(dǎo)入包,實(shí)際上就是基于工作目錄的文件夾目錄

導(dǎo)入包的多種方式:

  • 直接根據(jù)$GOPATH/src目錄導(dǎo)入import "test/lib"(路徑其實(shí)是$GOPATH/src/test/lib)
  • 別名導(dǎo)入:import alias_name "test/lib" ,這樣使用的時(shí)候,可以直接使用別名
  • 使用點(diǎn)號(hào)導(dǎo)入:import . "test/lib",作用是使用的時(shí)候直接省略包名
  • 使用下劃線導(dǎo)入:improt _ "test/lib",該操作其實(shí)只是引入該包。當(dāng)導(dǎo)入一個(gè)包時(shí),它所有的init()函數(shù)就會(huì)被執(zhí)行,但有些時(shí)候并非真的需要使用這些包,僅僅是希望它的init()函數(shù)被執(zhí)行而已。這個(gè)時(shí)候就可以使用_操作引用該包。即使用_操作引用包是無(wú)法通過(guò)包名來(lái)調(diào)用包中的導(dǎo)出函數(shù),而是只是為了簡(jiǎn)單的調(diào)用其init函數(shù)()。往往這些init函數(shù)里面是注冊(cè)自己包里面的引擎,讓外部可以方便的使用,例如實(shí)現(xiàn)database/sql的包,在init函數(shù)里面都是調(diào)用了sql.Register(name string, driver driver.Driver)注冊(cè)自己,然后外部就可以使用了。
  • 相對(duì)路徑導(dǎo)入     import   "./model"  //當(dāng)前文件同一目錄的model目錄,但是不建議這種方式import

首先,還是對(duì)上面的示例程序做一個(gè)更改,這次我們讓它變得更加簡(jiǎn)單點(diǎn),因?yàn)榻酉聛?lái)討論的東西,可能會(huì)稍微有點(diǎn)繞~~

首先,刪除hello2.go,清理掉編譯生成的文件,其他文件內(nèi)容如下:

hello.go

//hello.go
package hello

import (
  "fmt"
)

func SayHello() {
  fmt.Println("SayHello()-->Hello")
}

main.go

//main.go
package main

import (
  "hello"
)

func main() {
  hello.SayHello()
}

最后,讓整體保持如下的樣式:

我們先編譯一次,讓程序能夠運(yùn)行起來(lái):

go install hello
go build main
./main

好吧,假如你能看到輸出,那就沒(méi)問(wèn)題了!
此時(shí),再來(lái)看看整體的結(jié)構(gòu):

按照C/C++的方式來(lái)說(shuō),此時(shí)生成了hello.a這個(gè)鏈接庫(kù),那么源文件那些應(yīng)該就沒(méi)有必要了吧,所以。。。。我們這樣搞一下,我們來(lái)更改一下hello.go源碼,但不編譯它!
hello.go

//hello.go
package hello

import (
  "fmt"
)

func SayHello() {
  fmt.Println("SayHello()-->Hello_modifi...")
}

然后,我們刪除之前的可執(zhí)行文件main,再重新生成它:

rm main
go build main

恩~~等等,我看一下運(yùn)行結(jié)果:

What the fuck!??!為什么出來(lái)的是這貨???

好吧,為了一探究竟,我們?cè)俅蝿h除main文件,并再次重新編譯,不過(guò)命令上得做點(diǎn)手腳,我們要看看編譯器連接器這兩個(gè)小婊砸到底都干了些什么,為啥是隔壁老王的兒子出來(lái)了????!

rm main
go build -x -v main

結(jié)果:

那么我們一步一步對(duì)這個(gè)結(jié)果做一個(gè)分析:

#首先,它好像指定了一個(gè)臨時(shí)工作目錄
WORK=/tmp/go-build658882358 

#看著樣子,它好像是要準(zhǔn)備編譯hello目錄下的包
hello
#然后創(chuàng)建了一系列臨時(shí)文件夾
mkdir -p $WORK/hello/_obj/  
mkdir -p $WORK/

#進(jìn)入包的源文件目錄
cd /home/yuxuan/GoProjects/import/src/hello 

#調(diào)用6g這個(gè)編譯器編譯生成hello.a,存放在$WORK/臨時(shí)目錄下
/opt/go/pkg/tool/linux_amd64/6g -o $WORK/hello.a -trimpath $WORK -p hello -complete -D _/home/yuxuan/GoProjects/import/src/hello -I $WORK -pack ./hello.go

#要編譯main目錄下的包了
main
#還是創(chuàng)建一系列的臨時(shí)文件夾
mkdir -p $WORK/main/_obj/  
mkdir -p $WORK/main/_obj/exe/

#進(jìn)入main文件夾
cd /home/yuxuan/GoProjects/import/src/main

#調(diào)用6g編譯器,編譯生成main.a,存放于$WORK/臨時(shí)目錄下
/opt/go/pkg/tool/linux_amd64/6g -o $WORK/main.a -trimpath $WORK -p main -complete -D _/home/yuxuan/GoProjects/import/src/main -I $WORK -I /home/yuxuan/GoProjects/import/pkg/linux_amd64 -pack ./main.go

#最后它進(jìn)入了一個(gè)“當(dāng)前目錄”,應(yīng)該就是我們執(zhí)行g(shù)o build命令的目錄
cd .

#調(diào)用連接器6l 然后它鏈接生成a.out,存放與臨時(shí)目錄下的$WORK/main/_obj/exe/文件夾中,但是在鏈接選項(xiàng)中并未直接發(fā)現(xiàn)hello.a
#從鏈接選項(xiàng):-L $WORK -L /home/yuxuan/GoProjects/import/pkg/linux_amd64中可以看出,連接器首先搜索了$WORK臨時(shí)目錄下的所有*.a文件,然后再去搜索/home/yuxuan/GoProjects/import/pkg/linux_amd64目錄下的*.a文件,可見(jiàn)原因
/opt/go/pkg/tool/linux_amd64/6l -o $WORK/main/_obj/exe/a.out -L $WORK -L /home/yuxuan/GoProjects/import/pkg/linux_amd64 -extld=gcc $WORK/main.a

#最后,移動(dòng)可執(zhí)行文件并重命名
mv $WORK/main/_obj/exe/a.out main

到這里,其實(shí)差不多也就得出結(jié)論了,連接器在連接時(shí),其實(shí)使用的并不是我們工作目錄下的hello.a文件,而是以該最新源碼編譯出的臨時(shí)文件夾中的hello.a文件。

當(dāng)然,如果你對(duì)這個(gè)結(jié)論有所懷疑,可以試試手動(dòng)執(zhí)行上述命令,在最后鏈接時(shí),去掉-L $WORK的選項(xiàng),再看看運(yùn)行結(jié)果!

那么,這是對(duì)于有源代碼的第三方庫(kù),如果沒(méi)有源代碼呢?

其實(shí),結(jié)果顯而易見(jiàn),沒(méi)有源代碼,上面的臨時(shí)編譯不可能成功,那么臨時(shí)目錄下就不可能有.a文件,所以最后鏈接時(shí)就只能鏈接到工作目錄下的.a文件!

但是,如果是自帶的Go標(biāo)準(zhǔn)庫(kù)呢?

其實(shí)也可以用上述的方法驗(yàn)證一下,驗(yàn)證過(guò)程就不寫了吧?
最后得到的結(jié)果是:對(duì)于標(biāo)準(zhǔn)庫(kù),即便是修改了源代碼,只要不重新編譯Go源碼,那么鏈接時(shí)使用的就還是已經(jīng)編譯好的*.a文件!

3 導(dǎo)入包的三種模式

包導(dǎo)入有三種模式:正常模式、別名模式、簡(jiǎn)便模式

Go language specification中關(guān)于import package時(shí)列舉的一個(gè)例子如下:

Import declaration Local name of Sin

import “l(fā)ib/math” math.Sin 
import m “l(fā)ib/math” m.Sin 
import . “l(fā)ib/math” Sin

我們看到import m “l(fā)ib/math” m.Sin一行,在上面的結(jié)論中說(shuō)過(guò)lib/math是路徑,import語(yǔ)句用m替代lib/math,并在代碼中通過(guò)m訪問(wèn)math包中導(dǎo)出的函數(shù)Sin。
那m到底是包名還是路徑呢?
答案顯而易見(jiàn),能通過(guò)m訪問(wèn)Sin,那m肯定是包名了!
那問(wèn)題又來(lái)了,import m “l(fā)ib/math”該如何理解呢?

根據(jù)上面得出的結(jié)論,我們嘗試這樣理解m:m指代的是lib/math路徑下唯一的那個(gè)包!

4 總結(jié)

經(jīng)過(guò)上面這一長(zhǎng)篇大論,是時(shí)候該總結(jié)一下成果了:

多個(gè)源文件可同屬于一個(gè)包,只要聲明時(shí)package指定的包名一樣;一個(gè)包對(duì)應(yīng)生成一個(gè)*.a文件,生成的文件名并不是包名+.a組成,應(yīng)該是目錄名+.a組成go install ××× 這里對(duì)應(yīng)的并不是包名,而是路徑名??!import ××× 這里使用的也不是包名,也是路徑名×××××.SayHello() 這里使用的才是包名!指定×××路徑名就代表了此目錄下唯一的包,編譯器連接器默認(rèn)就會(huì)去生成或者使用它,而不需要我們手動(dòng)指明!一個(gè)目錄下就只能有一個(gè)包存在對(duì)于調(diào)用有源碼的第三方包,連接器在連接時(shí),其實(shí)使用的并不是我們工作目錄下的.a文件,而是以該最新源碼編譯出的臨時(shí)文件夾中的.a文件對(duì)于調(diào)用沒(méi)有源碼的第三方包,上面的臨時(shí)編譯不可能成功,那么臨時(shí)目錄下就不可能有.a文件,所以最后鏈接時(shí)就只能鏈接到工作目錄下的.a文件對(duì)于標(biāo)準(zhǔn)庫(kù),即便是修改了源代碼,只要不重新編譯Go源碼,那么鏈接時(shí)使用的就還是已經(jīng)編譯好的*.a文件包導(dǎo)入有三種模式:正常模式、別名模式、簡(jiǎn)便模式

到此這篇關(guān)于Golang import本地包和導(dǎo)入問(wèn)題相關(guān)詳解的文章就介紹到這了,更多相關(guān)Golang import包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • 解決Goland 提示 Unresolved reference 錯(cuò)誤的問(wèn)題
  • go語(yǔ)言入門環(huán)境搭建及GoLand安裝教程詳解
  • 淺談goland導(dǎo)入自定義包時(shí)出錯(cuò)(一招解決問(wèn)題)

標(biāo)簽:銅川 湘潭 蘭州 仙桃 黃山 崇左 湖南 衡水

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Golang import本地包和導(dǎo)入問(wèn)題相關(guān)詳解》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266