您好,登錄后才能下訂單哦!
本篇內容主要講解“go語言基本語法有哪些”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“go語言基本語法有哪些”吧!
一、描述
go語言是直接將源碼編譯成二進制機器碼的語言;它支持面向對象、也支持函數式編程;go是強類型語言;Go 編程語言原生支持并發。Go 使用 Go 協程(Goroutine) 和信道(Channel)來處理并發。
二、基本語法
1、切片與數組差別:切片是動態化數組,切片是注意兩者初始化和函數的區別
a.初始化差別
數組需要指定大小,不指定也會根據初始化的自動推算出大小,不可改變
a:=[...] int {1,2,3} ; a:=[3] int {1,2,3}
切片不需要指定大小
a:=[] int {1,2,3}; a:=make([]int,3); a:=make([] int ,3,5)
b.函數傳遞:
數組需要明確指定大小,數組是值傳遞,不同的數組總是代表不同的存儲。
切片不需要明確指定大小,切片是地址傳遞。多個切片如果表示同一個數組的片段,它們可以共享數據;因此一個切片和相關數組的其他切片是共享存儲的。
func changeArray(a [3]int) {//數組
a[0] = 100
}
func changeSlice(s []int) {//切片
s[0] = 100
}
func main(){
a := [...]int{1, 2, 3}
changeArray(a)
fmt.Println(a[0]) //值傳遞,輸出結果:1
var s []int = []int{1, 2, 3, 4}
fmt.Println(len(s), cap((s)))
s = append(s, 6, 7, 8)
fmt.Println(len(s), cap(s))
changeSlice(s)
fmt.Println(s[0]) //地址傳遞,輸出結果:100
}
c.切片和垃圾回收
切片的底層指向一個數組,該數組的實際容量可能要大于切片所定義的容量。只有在沒有任何切片指向的時候,底層的數組內層才會被釋放,這種特性有時會導致程序占用多余的內存。
示例 函數 FindDigits
將一個文件加載到內存,然后搜索其中所有的數字并返回一個切片。
var digitRegexp = regexp.MustCompile("[0-9]+") func FindDigits(filename string) []byte { b, _ := ioutil.ReadFile(filename) return digitRegexp.Find(b) }
這段代碼可以順利運行,但返回的 []byte
指向的底層是整個文件的數據。只要該返回的切片不被釋放,垃圾回收器就不能釋放整個文件所占用的內存。換句話說,一點點有用的數據卻占用了整個文件的內存。
想要避免這個問題,可以通過拷貝我們需要的部分到一個新的切片中:
func FindDigits(filename string) []byte { b, _ := ioutil.ReadFile(filename) b = digitRegexp.Find(b) c := make([]byte, len(b)) copy(c, b) return c }
2.數組和Map,數組的輸出按照數組的放置順序,但是Map是無序輸出的,每次遍歷的順序不一樣;
var m = map[string]int{ "unix": 0, "python": 1, "go": 2, "javascript": 3, "testing": 4, "philosophy": 5, "startups": 6, "productivity": 7, "hn": 8, "reddit": 9, "C++": 10, } var keys []string for k := range m { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { fmt.Println("Key:", k, "Value:", m[k]) }
3.go語言中的指針
指針是一種存儲變量內存地址(Memory Address)的變量。
如上圖所示,變量 b
的值為 156
,而 b
的內存地址為 0x1040a124
。變量 a
存儲了 b
的地址。我們就稱 a
指向了 b
。
指針變量的類型為 *T
,該指針指向一個 T 類型的變量。
&b 獲取b的內存地址值;*a 則是獲取a值對應物理地址值的b值:156;
向函數傳遞指針參數
func change(val *int) { *val = 55 } func main() { a := 58 fmt.Println("value of a before function call is",a) b := &a change(b) fmt.Println("value of a after function call is", a) }
4.那么什么時候使用指針接收器,什么時候使用值接收器?
/* 使用值接收器的方法。 */ func (e Employee) changeName(newName string) { e.name = newName } /* 使用指針接收器的方法。 */ func (e *Employee) changeAge(newAge int) { e.age = newAge }
一般來說,指針接收器可以使用在:對方法內部的接收器所做的改變應該對調用者可見時。
指針接收器也可以被使用在如下場景:當拷貝一個結構體的代價過于昂貴時。考慮下一個結構體有很多的字段。在方法內使用這個結構體做為值接收器需要拷貝整個結構體,這是很昂貴的。在這種情況下使用指針接收器,結構體不會被拷貝,只會傳遞一個指針到方法內部使用。
在其他的所有情況,值接收器都可以被使用。
5.在方法中使用值接收器 與 在函數中使用值參數
這個話題很多Go語言新手都弄不明白。我會盡量講清楚。
當一個函數有一個值參數,它只能接受一個值參數。
當一個方法有一個值接收器,它可以接受值接收器和指針接收器。
讓我們通過一個例子來理解這一點。
package main import ( "fmt" ) type rectangle struct { length int width int } func area(r rectangle) { fmt.Printf("Area Function result: %d\n", (r.length * r.width)) } func (r rectangle) area() { fmt.Printf("Area Method result: %d\n", (r.length * r.width)) } func main() { r := rectangle{ length: 10, width: 5, } area(r) r.area() p := &r /* compilation error, cannot use p (type *rectangle) as type rectangle in argument to area */ //area(p) p.area()//通過指針調用值接收器 }
6.在方法中使用指針接收器 與 在函數中使用指針參數
和值參數相類似,函數使用指針參數只接受指針,而使用指針接收器的方法可以使用值接收器和指針接收器。
package main import ( "fmt" ) type rectangle struct { length int width int } func perimeter(r *rectangle) { fmt.Println("perimeter function output:", 2*(r.length+r.width)) } func (r *rectangle) perimeter() { fmt.Println("perimeter method output:", 2*(r.length+r.width)) } func main() { r := rectangle{ length: 10, width: 5, } p := &r //pointer to r perimeter(p) p.perimeter() /* cannot use r (type rectangle) as type *rectangle in argument to perimeter */ //perimeter(r) r.perimeter()//使用值來調用指針接收器 }
7.并行和并發
并行不一定會加快運行速度,因為并行運行的組件之間可能需要相互通信。在我們瀏覽器的例子里,當文件下載完成后,應當對用戶進行提醒,比如彈出一個窗口。于是,在負責下載的組件和負責渲染用戶界面的組件之間,就產生了通信。在并發系統上,這種通信開銷很小。但在多核的并行系統上,組件間的通信開銷就很高了。所以,并行不一定會加快運行速度!
8.線程與協程區別
線程切換從系統層面遠不止 保存和恢復 CPU上下文這么簡單。操作系統為了程序運行的高效性每個線程都有自己緩存Cache等等數據,操作系統還會幫你做這些數據的恢復操作。所以線程的切換非常耗性能。但是協程的切換只是單純的操作CPU的上下文,所以一秒鐘切換個上百萬次系統都抗的住。但是協程有一個問題,就是系統并不感知,所以操作系統不會幫你做切換。目前的協程框架一般都是設計成 1:N 模式。所謂 1:N 就是一個線程作為一個容器里面放置多個協程。那么誰來適時的切換這些協程?答案是有協程自己主動讓出CPU,也就是每個協程池里面有一個調度器,這個調度器是被動調度的。意思就是他不會主動調度。協程的切換很輕。
9.緩沖信道和非緩沖信道
緩沖的channel:保證往緩沖中存數據先于對應的取數據,簡單說就是在取的時候里面肯定有數據,否則就因取不到而阻塞。
非緩沖的channel:保證取數據先于存數據,就是保證存的時候肯定有其他的goroutine在取,否則就因放不進去而阻塞
到此,相信大家對“go語言基本語法有哪些”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。