這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
go語言菜鳥教程:http://www.runoob.com/go/go-tutorial.html
實效go編程:http://docscn.studygolang.com/doc/effective_go.html
線上go運行環境:https://tour.golang.org/welcome/1
go的安裝:參照這裡在Windows上安裝非常easy:http://www.runoob.com/go/go-environment.html
go中文標準庫(Ctrl+f):http://docscn.studygolang.com/pkg/#stdlib
go英文包索引與搜尋引擎:https://godoc.org/
/**********************************************************************************************************************/
gofmt 程式將Go程式按照標準風格縮排、 對齊,保留注釋並在需要時重新格式化。若你想知道如何處理一些新的代碼布局,請嘗試運行 gofmt;
分號並不在源碼中出現。詞法分析器會自動插入分號。可以概括為: “如果新行前的標記為語句的末尾,則插入分號”。左大括弧不能放在下一行,如果這樣做,就會在大括弧前面插入一個分號,這可能引起不需要的效果。
Go不再使用 do 或 while 迴圈,只有一個更通用的 for;switch 要更靈活一點;if 和 switch 像 for一樣可接受可選的初始化語句; 此外,還有一個包含類型選擇和多路通訊複用器的新控制結構:select。 其文法也有些許不同:沒有圓括弧,而其主體必須始終使用大括弧括住。
在變數與運算子間加入空格,程式看起來更加美觀。
Go 語言資料類型
布爾型
布爾型的值只可以是常量 true 或者 false。一個簡單的例子:var b bool = true。
數字類型
整型 int 和浮點型 float,Go 語言支援整型和浮點型數字,並且原生支援複數,其中位的運算採用補碼。
Go 也有基於架構的類型,例如:int、uint 和 uintptr。uint8uint16uint32uint64int8int16int32int64
浮點型:float32、float64。complex64(32 位實數和虛數)complex128(64 位元實數和虛數)
字串類型:
字串就是一串固定長度的字元串連起來的字元序列。Go的字串是由單個位元組串連起來的。Go語言的字串的位元組使用UTF-8編碼標識Unicode文本。
Go 語言變數
Go 語言變數名由字母、數字、底線組成,其中首個字母不能為數字。
聲明變數的一般形式是使用 var 關鍵字。
第一種,指定變數類型,聲明後若不賦值,使用預設值,null 指標為nil。
var name typename = value
第二種,根據值自行判定變數類型
var name = valuevar e, f = 123, "hello"
第三種,省略var, 注意 :=左側的變數不應該是已經聲明過的,否則會導致編譯錯誤。這種不帶聲明格式的只能在函數體中出現。由系統自動推斷,初始化聲明。
name := value//只能在函數體中出現// 例如var a int = 10var b = 10c : = 10
第四種,多變數聲明(重要)
//類型相同多個變數, 非全域變數var vname1, vname2, vname3 typevname1, vname2, vname3 = v1, v2, v3var vname1, vname2, vname3 = v1, v2, v3 //和python很像,不需要顯示宣告類型,自動推斷vname1, vname2, vname3 := v1, v2, v3 //出現在:=左側的變數不應該是已經被聲明過的,否則會導致編譯錯誤// 這種因式分解關鍵字的寫法一般用於聲明全域變數var ( vname1 v_type1 vname2 v_type2)
實值型別和參考型別
所有像 int、float、bool 和 string 這些基本類型都屬於實值型別,使用這些類型的變數直接指向存在記憶體中的值。當使用等號 = 將一個變數的值賦值給另一個變數時,如:j = i,實際上是在記憶體中將 i 的值進行了拷貝。
如果你聲明了一個局部變數卻沒有在相同的代碼塊中使用它,同樣會得到編譯錯誤(a declared and not used) 但是全域變數是允許聲明但不使用。
多變數(已申明)可以在同一行進行賦值,如:
a, b, c = 5, 7, "abc"//未聲明,用:=a, b, c := 5, 7, "abc"
空白標識符 _ 也被用於拋棄值,如值 5 在:_, b = 5, 7 中被拋棄。_ 實際上是一個唯寫變數,你不能得到它的值。這樣做是因為 Go 語言中你必須使用所有被聲明的變數,但有時你並不需要使用從一個函數得到的所有傳回值。
Go 語言常量
const identifier [type] = value
常量還可以用作枚舉:
const ( Unknown = 0 Female = 1 Male = 2)
神奇的iota
/*iota,特殊常量,可以認為是一個可以被編譯器修改的常量。在每一個const關鍵字出現時,被重設為0,然後再下一個const出現之前,每出現一次iota,其所代表的數字會自動增加1。iota 可以被用作枚舉值:*/const ( a = iota b = iota c = iota)//簡寫形式const ( a = iota b c)const ( a = iota //0 b //1 c //2 d = "ha" //獨立值,iota += 1 e //"ha" iota += 1 f = 100 //iota +=1 g //100 iota +=1 h = iota //7,恢複計數 i //8 )
一個更神奇的用法:
package mainimport "fmt"const (i=1<<iota // i=1<<0 j=3<<iota // j=3<<1 k // k=3<<2 12 l // j=3<<3 24)func main() {fmt.Println("i=",i)fmt.Println("j=",j)fmt.Println("k=",k)fmt.Println("l=",l)}//以上運行結果為:i= 1j= 6k= 12l= 24
邏輯運算子&& || !就是判斷真假的
位元運算符&, |, 和 ^
位元運算符對整數在記憶體中的二進位位進行操作。對一個32位負數取非符號位的其他位a & 0x7fffffff
Go 語言 switch 語句
變數 var1 可以是任何類型,而 val1 和 val2 則可以是同類型的任意值。匹配項後面也不需要再加break,運行完後結束。
//Go 程式設計語言中 switch 語句的文法如下:switch var1 { case val1: ... case val2: ... default: ...}package mainimport "fmt"func main() { /* 定義局部變數 */ var grade string = "B" var marks int = 90 switch marks { case 90: grade = "A" case 80: grade = "B" case 50,60,70 : grade = "C" default: grade = "D" } switch { case grade == "A" : fmt.Printf("優秀!\n" ) case grade == "B", grade == "C" : fmt.Printf("良好\n" ) case grade == "D" : fmt.Printf("及格\n" ) case grade == "F": fmt.Printf("不及格\n" ) default: fmt.Printf("差\n" ); } fmt.Printf("你的等級是 %s\n", grade ); }
Go 語言 select 語句
select是Go中的一個控制結構,類似於用於通訊的switch語句。每個case必須是一個通訊操作,要麼是發送要麼是接收。select隨機執行一個可啟動並執行case。如果沒有case可運行,它將阻塞,直到有case可運行。一個預設的子句應該總是可啟動並執行。
以下描述了 select 語句的文法:
*每個case都必須是一個通訊*所有channel運算式都會被求值*所有被發送的運算式都會被求值*如果任意某個通訊可以進行,它就執行;其他被忽略。*如果有多個case都可以運行,Select會隨機公平地選出一個執行。其他不會執行。 *否則:*如果有default子句,則執行該語句。*如果沒有default字句,select將阻塞,直到某個通訊可以運行;Go不會重新對channel或值進行求值。
package mainimport "fmt"func main() { var c1, c2, c3 chan int var i1, i2 int select { case i1 = <-c1: fmt.Printf("received ", i1, " from c1\n") case c2 <- i2: fmt.Printf("sent ", i2, " to c2\n") case i3, ok := (<-c3): // same as: i3, ok := <-c3 if ok { fmt.Printf("received ", i3, " from c3\n") } else { fmt.Printf("c3 is closed\n") } default: fmt.Printf("no communication\n") } }
Go 語言 for 迴圈
搭配break,continue使用;Go語言的For迴圈有3中形式,只有其中的一種使用分號:
//和 C 語言的 for 一樣:for init; condition; post { }//和 C 的 while 一樣,取代while:for condition { }//和 C 的 for(;;) 一樣:for { }
for 迴圈的 range 格式可以對 slice、map、數組、字串等進行迭代迴圈。格式如下:
for key, value := range oldMap { newMap[key] = value}
Go 語言函數
Go 語言函數定義格式如下:
func f(參數) (傳回值) { 函數體}//返回多個值func swap(x, y string) (string, string) { return y, x}/* 函數返回兩個數的最大值 */func max(num1, num2 int) int { var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }
函數做為值使用:
package mainimport ( "fmt" "math")func main(){ getSquareRoot := func(x float64) float64 { return math.Sqrt(x) } fmt.Println(getSquareRoot(9))}
Go 語言匿名函數
func(參數列表) (傳回值列表) { 函數體... }
//直接使用,無參數直接加括弧 func() int { var i int =5 fmt.Printf("func 1\n") return i }() //直接使用,有參數,在括弧裡加參數 func(arge int) { fmt.Printf("func %d\n",arge) }(2) //也可以先賦給一個變數再調用 a := func() int { fmt.Printf("func 3\n") return 5 } a()
Go 語言數組
- 數組是值。將一個數組賦予另一個數組會複製其所有元素。
- 切片儲存了對底層數組的引用,若你將某個切片賦予另一個切片,它們會引用同一個數組。
- 特別地,若將某個數組傳入某個函數,它將接收到該數組的一份副本而非指標。
- 數組的大小是其類型的一部分。類型
[10]int 和 [20]int 是不同的。
Go 語言數組聲明需要指定元素類型及元素個數,文法格式如下:
var name [SIZE] type//var a [10] int//數組初始化:var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}//如果忽略[]中的數字不設定數組大小,Go 語言會根據元素的個數來設定數組的大小:var balance = []float32{1000.0, 2.0, 3.4, 7.0, 50.0}
Go 語言多維陣列
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type//var threedim [5][10][4]intvar a = [3][4]int{ {0, 1, 2, 3} , /* 第一行索引為 0 */ {4, 5, 6, 7} , /* 第二行索引為 1 */ {8, 9, 10, 11} /* 第三行索引為 2 */}
Go 語言向函數傳遞數組
//指定大小void myFunction(param [10]int){...}//不指定大小void myFunction(param []int){...}//例子func getAverage(arr []int, size int) float32 { var i,sum int var avg float32 for i = 0; i < size;i++ { sum += arr[i] } avg = float32(sum / size) return avg;}
Go 語言指標
//指標聲明格式如下:var name *type//列印指標地址%x:fmt.Printf("變數的地址: %x\n", &a )//Go null 指標,它的值為 nilif(ptr == nil)
Go 語言指標數組
聲明整型指標數組(數組每個元素都是指標):var ptr [3]*int;
Go 語言指向指標的指標
var ptr **int;
Go 語言指標作為函數參數
func swap(x *int, y *int) { var temp int temp = *x /* 儲存 x 地址的值 */ *x = *y /* 將 y 賦值給 x */ *y = temp /* 將 temp 賦值給 y */
Go 語言結構體
定義結構體和聲明變數
//結構體定義需要使用 type 和 struct 語句。//type 語句設定了結構體的名稱。結構體的格式如下:type my_type struct { name1 type1 name2 type2 ...}一旦定義了結構體類型,它就能用於變數的聲明,文法格式如下:name := my_type {value1, value2...valuen}//例子:type Books struct { title string author string subject string book_id int}var Book1 BooksBook1.title = "Go 語言"Book1.author = "www.runoob.com"Book1.subject = "Go 語言教程"Book1.book_id = 6495407//結構體作為函數參數func printBook( book Books ) {}//結構體指標var ptr *Books
Go 語言切片(Slice)
Go 語言切片是對數組的抽象。Go 數組的長度不可改變。切片("動態數組"),長度是不固定的
切片儲存了對底層數組的引用,若你將某個切片賦予另一個切片,它們會引用同一個數組。
聲明一個未指定大小的數組來定義切片:var name []type或使用make()函數來建立切片:var slice1 []type = make([]type, len)或者make([]T, length, capacity)//capacity是選擇性參數也可以簡寫為:slice1 := make([]type, len)/************************************/s :=[] int {1,2,3 } //直接初始化s := arr[:] //用數組初始化切片s := arr[startIndex:endIndex] //新切片fmt.Printf("slice=%v\n",x) //輸出切片2種方法 結果:slice=[0 0 0]fmt.Println("slice=", x)func printSlice(x []int){ //這個函數用於輸出切片資訊 fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)}if(numbers == nil){ //判斷空切片 fmt.Printf("切片是空的")}
切片截取
package mainimport "fmt"func main() { /* 建立切片 */ numbers := []int{0,1,2,3,4,5,6,7,8} printSlice(numbers) /* 列印原始切片 */ fmt.Println("numbers ==", numbers) /* 列印子切片從索引1(包含) 到索引4(不包含)*/ fmt.Println("numbers[1:4] ==", numbers[1:4]) /* 預設下限為 0*/ fmt.Println("numbers[:3] ==", numbers[:3]) /* 預設上限為 len(s)*/ fmt.Println("numbers[4:] ==", numbers[4:]) numbers1 := make([]int,0,5) printSlice(numbers1) /* 列印子切片從索引 0(包含) 到索引 2(不包含) */ number2 := numbers[:2] printSlice(number2) /* 列印子切片從索引 2(包含) 到索引 5(不包含) */ number3 := numbers[2:5] printSlice(number3)}func printSlice(x []int){ fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)}
切片append()和 copy()
var numbers []int /* 允許追加空切片 */ numbers = append(numbers, 0) /* 向切片添加一個元素 */ numbers = append(numbers, 1) /* 同時添加多個元素 */ numbers = append(numbers, 2,3,4) /* 建立切片 numbers1 是之前切片的兩倍容量*/ numbers1 := make([]int, len(numbers), (cap(numbers))*2) /* 拷貝 numbers 的內容到 numbers1 */ copy(numbers1,numbers)
Go 語言範圍(Range)
//range一個切片,返回下標和對應項nums := []int{2, 3, 4}sum := 0for _, num := range nums { sum += num} //range也可以用在map的索引值對上。 kvs := map[string]string{"a": "apple", "b": "banana"} for k, v := range kvs { fmt.Printf("%s -> %s\n", k, v) } //range也可以用來枚舉Unicode字串。 //第一個參數是字元的索引,第二個是字元(Unicode的值)本身。 for i, c := range "go" { fmt.Println(i, c) } //結果: 0 103 1 111
Go 語言Map(集合)
Map 是一種無序的索引值對的集合。Map 是無序的,我們無法決定它的返回順序,這是因為 Map 是使用 hash 表來實現的。
/* 聲明變數,預設 map 是 nil ,nil map 不能用來存放索引值對*/var myname map[ktype]vtype/* 使用 make 函數,之後才能添加元素 */myname = make(map[ktype]vtype)
//例子 var countryCapitalMap map[string]string /* 建立集合 */ countryCapitalMap = make(map[string]string) /* map 插入 key-value 對,各個國家對應的首都 */ countryCapitalMap["France"] = "Paris" countryCapitalMap["Italy"] = "Rome" countryCapitalMap["Japan"] = "Tokyo" countryCapitalMap["India"] = "New Delhi" /* 使用 key 輸出 map 值 */ for country := range countryCapitalMap { fmt.Println("Capital of",country,"is",countryCapitalMap[country]) } /* 查看元素在集合中是否存在 */ captial, ok := countryCapitalMap["United States"] /* 如果 ok 是 true, 則存在,否則不存在 */ if(ok){ fmt.Println("Capital of United States is", captial) }else { fmt.Println("Capital of United States is not present") }
map中刪除元素
//delete(), 參數為 map 和其對應的 key/* 刪除元素 */delete(countryCapitalMap,"France");
Go 語言類型轉換
type(myname) var a int = 17 var b int = 5 var c float32 c = float32(a)/float32(b) fmt.Printf("c 的值為: %f\n",c)
Go 語言new
Go提供了兩種分配原語,即內建函數 new 和 make。
new不會初始化記憶體,只會將記憶體置零。 也就是說,new(T) 會為類型為 T 的新項分配已置零的記憶體空間, 並返回它的地址,也就是一個類型為 *T 的值。
Go 語言make
內建函數 make(T, args) 的目的不同於 new(T)。
它只用於建立切片、映射和通道,並傳回型別為 T(而非 *T)的一個已初始化 (而非置零)的值。 出現這種用差異的原因在於,這三種類型本質上為引用資料類型,它們在使用前必須初始化。
make 只適用於映射、切片和通道且不返回指標。若要獲得明確的指標, 請使用 new 分配記憶體(如下)。
var p *[]int = new([]int) // 分配切片結構;*p == nil;基本沒用var v []int = make([]int, 100) // 切片 v 現在引用了一個具有 100 個 int 元素的新數組// 沒必要的複雜:var p *[]int = new([]int)*p = make([]int, 100, 100)// 習慣用法:v := make([]int, 100)
Go 錯誤處理
Go 語言通過內建的錯誤介面提供了非常簡單的錯誤處理機制。
我們可以在編碼中通過實現 error 介面類型來建置錯誤資訊。
函數通常在最後的傳回值中返回錯誤資訊。
error類型是一個介面類型,這是它的定義:
type error interface { Error() string}
package mainimport ("fmt")// 定義一個 DivideError 結構type DivideError struct {dividee intdivider int}// 實現`error`介面func (de *DivideError) Error() string {strFormat := `Cannot proceed, the divider is zero.dividee: %ddivider: 0`return fmt.Sprintf(strFormat, de.dividee)}// 定義 `int` 類型除法運算的函數func Divide(varDividee int, varDivider int) (result int, errorMsg string) {if varDivider == 0 {dData := DivideError{dividee: varDividee,divider: varDivider,}errorMsg = dData.Error()return} else {return varDividee / varDivider, ""}}func main() {// 正常情況if result, errorMsg := Divide(100, 10); errorMsg == "" {fmt.Println("100/10 = ", result)}// 當被除數為零的時候會返回錯誤資訊if _, errorMsg := Divide(100, 0); errorMsg != "" {fmt.Println("errorMsg is: ", errorMsg)}}