歡迎您光臨本站 註冊首頁

Go語言編程開發入門簡介

←手機掃碼閱讀     火星人 @ 2014-03-08 , reply:0

Go語言編程開發入門簡介: Go語言是google開發的一門新的系統級語言,項目主頁:http://golang.org/

1. 安裝go語言環境

目前go語言主要用於Linux環境,提供源代碼方式安裝.關係安裝的完整介紹請參考
官方網站: http://golang.org/doc/install.html.

這裡的文檔主要是針對Linux系統的用戶.go採用Mercurial管理代碼,因此需要現安裝
Mercurial工具.除此之外,go的編譯還依賴以下工具:bison/gcc/libc6-dev/ed/gawk
和make.

先創建一個空目錄,假設為 $HOME/go,可以用以下命令獲取源代碼:

代碼: 全選
$ hg clone -r release https://go.googlecode.com/hg/ $HOME/go
然後配置環境變數:

代碼: 全選
export GOROOT=$HOME/go
export GOARCH=386
export GOOS=linux
export GOBIN=$HOME/bin
其中GOARCH對應CPU類型,如果是32位的則為386,64位的為amd64,另外還有arm類型.
GOBIN對應go編譯工具放置的目錄,要確保對應的目錄存在.然後將GOBIN目錄添加到PATH中:

代碼: 全選
export PATH=$GOBIN:$PATH
用以下命令再查看一下環境變數是否已經設置好:

代碼: 全選
$ env | grep ‘^GO’
如果一切就緒的話就可以開始編譯go了:

代碼: 全選
$ cd $GOROOT/src
$ ./all.bash
如果編譯成功,可以看到以下結尾信息:
代碼: 全選
— cd ../test
N known bugs; 0 unexpected bugs
編譯成功后,可以試試godoc命令是否可用.

2. Hello, 世界

讓我們來看看經典的”Hello, World”程序在go中是什麼樣子:

代碼: 全選
package main

func main() {
print(“Hello, 世界\n”)
}
保存到hello.go文件中,然後用以下命令編譯:

代碼: 全選
8g hello.go
8l hello.8
./8.out
其中8g/8l是對應386類型CPU的編譯命令,如果是64位系統請將8替換為6.如果一切正常,
應該可以輸出結果:

代碼: 全選
Hello, 世界
關於go更多的語言細節,這裡先不展開了.讀者可以參考:

http://golang.org/doc/go_tutorial.html

3. 用make方式編譯

對於正式的工程,一般都用make方式編譯.Go語言中使用make非常簡單.我們先建一個Makefile
文件,內容如下:

代碼: 全選
include $(GOROOT)/src/Make.$(GOARCH)

TARG=hello
GOFILES=hello.go

include $(GOROOT)/src/Make.cmd
其中第一行和最后一行比較固定.TARG指定輸出文件名,GOFILES指定對應的go程序源文件.
我們這裡只有一個hello.go文件,輸出名為hello.

重新編譯,並安裝到 $GOBIN :

代碼: 全選
make clean
make
make install
如果要刪除所有的編譯結果和取消安裝:

代碼: 全選
make nuke
4. 構建自己的包

對於傳統的C語言,很多通用的代碼通常組織為庫.在go語言中庫以包的方式來組織.我們
現在用go中的fmt包來輸出”Hello, World”:

代碼: 全選
package main

import “fmt”

func main() {
fmt.Printf(“Hello, 世界\n”)
}
但是,我們想構建我自己的fmt包,怎麼辦呢?,我們可以同時有多個功能不相同的包.
我們假設自己的包都以mypkg為前綴,例如:mypkg/hello、mypkg/fmt等.

由於Printf是一個相對複雜的函數,我們現在目的只是演示如何製作包.因此,只實現一個
簡單的函數,函數本身沒有參數:

代碼: 全選


package hello

import “fmt”

func PrintHello() {
fmt.Printf(“Hello, 世界\n”)
}
將上面這段代碼保存到 ./mypkg/hello/hello.go 文件中.然後創建 ./mypkg/hello/Makefile
文件,內容如下:

代碼: 全選
include $(GOROOT)/src/Make.$(GOARCH)

TARG=mypkg/hello
GOFILES=hello.go

include $(GOROOT)/src/Make.pkg
其中TARG對應包的全名,這裡包含mypkg前綴.GOFILES對應庫的源文件.最后一行為Make.pkg,
表示工程為包.如果最后一行為Make.cmd,則表示為可執行文件,例如前面講到的.

然後我們編譯並安裝包mypkg/hello:

代碼: 全選
make
make install
安裝后我們可以發現go目錄多了 $GOROOT/pkg/linux_386/mypkg/hello.a 文件,對於我們
的創建的包.

5. 包測試

由於我們已經安裝了包mypkg/hello,因此可以像標準庫那樣使用:

代碼: 全選
package main

import “mypkg/hello”

func main() {
print(“hello,”)
hello.PrintHello()
}
那如何在不安裝包的情況下進行簡單測試呢.為了實現這個目的,我們需要完善包的Makefile文件.
新的 ./mypkg/hello/Makefile 文件在末尾增加了幾行代碼:

代碼: 全選
include $(GOROOT)/src/Make.$(GOARCH)

TARG=mypkg/hello
GOFILES=hello.go

include $(GOROOT)/src/Make.pkg

# Simple test programs

%: install %.go
$(GC) $*.go
$(LD) -o $@ $*.$O
在測試前我們先要刪除安裝的mypkg/hello包,用以下命令(刪除后最好確認一下):

代碼: 全選
cd mypkg/hello
make nuke
然後在mypkg/hello創建測試程序,對應helloapp.go文件:

代碼: 全選
package main

import “mypkg/hello”

func main() {
print(“hello,”)
hello.PrintHello()
}
然後直接編譯helloapp:

代碼: 全選
make helloapp
我們發現目錄中多了一個helloapp可執行文件.上面的命令在編譯helloapp的時候,會自動
編譯mypkg/hello/包.如果還有新的測試程序,名字為helloapp2,那麼可以用以下方式
編譯:

代碼: 全選
make helloapp2
make后的參數是可選的.當然,這裡的測試程序比較短小,只對應一個源文件.

6. gotest

前面的測試只是臨時的.如果要編寫更專業的單元測試,我們可以用go提供的gotest工具.

還是在./mypkg/hello/目錄,增加一個hello_test.go文件.注意:hello_test名字
是包名hello加_test後綴.hello_test.go內容如下:

代碼: 全選
package hello

import “testing”

func TestPrintHello(t *testing.T) {
// ok
}

func TestError(t *testing.T) {
t.Errorf(“my error”)
}
這裡的一些細節可以先不要深究.其中每個以Test為前綴的是要進行測試的函數.我們這裡有
兩個測試函數,其中第二個認為輸出了一個錯誤.

在當前目錄輸入gotest進行測試,輸出以下結果:

代碼: 全選
[chai@localhost hello]$ gotest
rm -f _test/mypkg/hello.a _gotest_.8
/home/chai/bin/8g -o _gotest_.8 hello.go hello_test.go
rm -f _test/mypkg/hello.a
/home/chai/bin/gopack grc _test/mypkg/hello.a _gotest_.8
— FAIL: hello.TestError
my error
FAIL
7. 使用C語言函數

Go語言編程開發入門簡介 go編譯器有兩套:go自帶的和gccgo.其中gccgo是以gcc為後端,編譯后的go代碼可以和
gcc編譯的C/C 代碼集成.go自帶的編譯器中有一個叫cgo的工具,可以用於在go中集成C


語言庫.

在go中如果需要訪問C語言函數,一般是先將C語言函數包裝成go的包.前面我們已經講了如何
構建一個包,現在我們演示如何在包中訪問C語言函數.

我們現在創建和mypkg/hello功能類似的包:mypkg/hello2.hello2.go的內容如下:

代碼: 全選
package hello2

/*
#include
*/
import “C”

func PrintHello() {
C.puts(C.CString(“Hello, world\n”))
}
在這個例子中我們使用C語言的puts函數輸出結果.由於C語言不支持UTF8,我們這裡只輸出
英文字母.這裡的import行用於引入C語言庫,在該指令之前緊挨著的註釋會被當作C語言編譯.
然後,C語言的函數可以通過加C.前綴的方式來使用,例如:C.puts.

由於GO語言的不同數據類型之間是不能轉換的,因此將go的數據類型傳遞給C語言的數據類型
需要強制轉換.C語言的類型和C語言函數的使用方式類似,也要用C.前綴,例如:C.int、
C.float等.需要注意的是go中字元串和C語言中的字元指針是不同的,因此有一個專門的函數
C.CString用於將go字元串轉換為C語言字元串.

另外,目前cgo不支持從go中訪問C語言中可變參數的函數(例如printf).如果將上目的puts
換為printf函數,那麼在編譯的時候可能得到以下錯誤提示:

代碼: 全選
unexpected type: …
其中三個點為printf函數聲明中表示可變參數的部分.

Makefile文件也要作相應的更新,主要是將GOFILES該為CGOFILES.CGOFILES對應有
C語言代碼的go文件,用cgo編譯.如果工程中有純go的代碼,則還是對於GOFILES.改動
后的Makefile如下:

代碼: 全選
include $(GOROOT)/src/Make.$(GOARCH)

TARG=mypkg/hello2

CGOFILES=hello2.go

include $(GOROOT)/src/Make.pkg

# Simple test programs

%: install %.go
$(GC) $*.go
$(LD) -o $@ $*.$O
go是支持垃圾內存自動回收的.對於C語言函數中申請的內存,也應該有C語言模塊負責回收.
對於C.CString返回的字元串空間由go還是C語言負責回收還不清楚.

當然,將go中的數組傳遞給C語言也是可以的.例如,下面的代碼用C語言函數列印數組:

代碼: 全選
package hello2

/*
#include

int printArray(void *p, int len) {
int *v = (int*)p;
int i;

for(i = 0; i < len; i ) {
printf(“v[%d]: %d\n”, i, v[i]);
}
return i;
}
*/
import “C”
import “unsafe”
import “fmt”

func PrintHello() {
C.puts(C.CString(“Hello, world\n”))
}

func PrintSlice(v []int) {
n := C.printArray(unsafe.Pointer(&v[0]), C.int(len(v)))
fmt.Printf(“n = %d\n”, int(n))
}
我們在import “C”前面的註釋中定義了一個printArray函數,用C語言方式列印數組.
然後在PrintSlice中通過C.printArray方式調用,unsafe.Pointer(&v[0])
用於將go中的數組地址轉換為C語言的void指針,C.int(len(v))指定數組的長度.

然後將函數返回值保存在變數n中,數據類型為C.int.在輸出n的時候,我們強制轉換為
go語言的int類型.

測試函數為hello2app.go,內容如下:

代碼: 全選
package main

import “mypkg/hello2″

func main() {
print(“hello,”)
hello2.PrintHello()

hello2.PrintSlice([]int{ 3, 5, 1})
}
編譯運行后輸出以下結果:

代碼: 全選
[chai@localhost hello2]$ ./hello2app
hello,Hello, world

v[0]: 3
v[1]: 5
v[2]: 1
n = 3
如果我們需要在C語言函數中分配空間,然後在go中以數組的方式來訪問,可以用以下方式:

代碼: 全選
/*
void* getString() {
return “test string”;
}
*/
import “C”
import “unsafe”

func TryGetString
p := (*[100]uint8)(unsafe.Pointer(C.getString()))
fmt.Printf(“ss: %v\n”, string(p[0:10]))
}

這樣我們就可以實現go和C語言之間指針的雙向傳遞了.出了指針之外,go中還可以訪問C語言的
結構體.例如:

代碼: 全選
/*
typedef struct {
int a;
} CStruct;
*/
import “C”
import “fmt”

func TryStruct() {
v := C.CStruct{ a:10 }
fmt.Printf(“v struct: %v\n”, v2)
}


[火星人 ] Go語言編程開發入門簡介已經有760次圍觀

http://coctec.com/docs/linux/show-post-45723.html