文章目錄
- 1.程式結構
-
- 1.命名
- 2.聲明
- 3.變量
- 4.指派
- 5.類型
- 6.包和檔案
- 7.作用域
- 2.基礎資料類型
-
- 1.整型
- 2.浮點數
- 3.複數
- 4.布爾型
- 5.字元串
-
- 1.基本特性
- 2.字元串和Byte切片
- 3.字元串與數字的轉換
- 6.常量
-
- 1.基本概念
- 2.iota常量生成器
- 3.無類型的常量
1.程式結構
1.命名
關鍵字和内建字
2.聲明
- var:變量的聲明
- const:常量的聲明
- type:類型的聲明
- func:函數實體對象的聲明
3.變量
變量聲明(以字元串為例)
var a string
a = "123"
變量預設值
- 數值類型變量對應的零值是0
- 布爾類型變量對應的零值是false
- 字元串類型對應的零值是空字元串
- 接口或引用類型(包括slice、map、chan和函數)變量對應的零值是nil
- 數組或結構體等聚合類型對應的零值是每個元素或字段都是對應該類型的零值。
注意點
“:=”是一個變量聲明語句,而“=‘是一個變量指派操作
指針
一個變量對應一個儲存了變量對應類型值的記憶體空間。一個指針的值是另一個變量的位址。一個指針對應變量在記憶體中的存儲位置。
任何類型的指針的零值都是nil。如果 p != nil 測試為真,那麼p是指向某個有效變量。指針之間也是可以進行相等測試的,隻有當它們指向同一個變量或全部是nil時才相等。
指針包含了一個變量的位址,是以如果将指針作為參數調用函數,那将可以在函數中通過該指針來更新變量的值。
寫法:
p := &x // p是指向x的指針
*p = 2 // *p代表x,這個表達式等價于x = 2
new函數
表達式new(T)将建立一個T類型的匿名變量,初始化為T類型的零值,然後傳回變量位址,傳回的指針類型為 *T 。
每次調用new函數都是傳回一個新的變量的位址。
變量的回收思路
從每個包級的變量和每個目前運作函數的每一個局部變量開始,通過指針或引用的通路路徑周遊,是否可以找到該變量。如果不存在這樣的通路路徑,那麼說明該變量是不可達的,也就是說它是否存在并不會影響程式後續的計算結果。
因為一個變量的有效周期隻取決于是否可達,是以一個循環疊代内部的局部變量的生命周期可能超出其局部作用域。同時,局部變量可能在函數傳回之後依然存在。
關于go的垃圾回收和堆上配置設定、棧上配置設定的說法,可參考下面:
4.指派
元組指派
元組指派也可以使一系列瑣碎指派更加緊湊,比如:
i, j, k = 2, 3, 5
但如果表達式太複雜的話,應該盡量避免過度使用元組指派;因為每個變量單獨指派語句的寫法可讀性會更好。
有一種函數用額外的傳回值來表達某種錯誤類型,比如:
v, ok = m[key] // map lookup
v, ok = x.(T) // type assertion
v, ok = <-ch // channel receive
可指派性
指派語句是顯式的指派形式,但是程式中還有很多地方會發生隐式的指派行為:函數調用會隐式地将調用參數的值指派給函數的參數變量,一個傳回語句将隐式地将傳回操作的值指派給結果變量,一個複合類型的字面量(§4.2)也會産生指派行為。
對于兩個值是否可以用 == 或 != 進行相等比較的能力也和可指派能力有關系:對于任何類型的值的相等比較,第二個值必須是對第一個值類型對應的變量是可指派的,反之依然。
5.類型
一個類型聲明語句建立了一個新的類型名稱,和現有類型具有相同的底層結構。新命名的類型提供了一個方法,用來分隔不同概念的類型,這樣即使它們底層類型相同也是不相容的:
type 類型名字 底層類型
對于每一個類型T,都有一個對應的類型轉換操作T(x),用于将x轉為T類型(譯注:如果T是指針類型,可能會需要用小括弧包裝T,比如 (*int)(0) )。隻有當兩個類型的底層基礎類型相同時,才允許這種轉型操作,或者是兩者都是指向相同底層結構的指針類型,這些轉換隻改變類型而不會影響值本身。
6.包和檔案
包的初始化
包的初始化首先是解決包級變量的依賴順序,然後安照包級變量聲明出現的順序依次初始化
對于在包級别聲明的變量,如果有初始化表達式則用表達式初始化,還有一些沒有初始化表達式的,可以用一個特殊的init初始化函數來簡化初始化工作。每個檔案都可以包含多個init初始化函數
這樣的init初始化函數除了不能被調用或引用外,其他行為和普通函數類似。在每個檔案中的init初始化函數,在程式開始執行時按照它們聲明的順序被自動調用。
每個包在解決依賴的前提下,以導入聲明的順序初始化,每個包隻會被初始化一次。
初始化工作是自下而上進行的,main包最後被初始化。以這種方式,可以確定在main函數執行之前,所有依然的包都已經完成初始化工作了。
7.作用域
不要将作用域和生命周期混為一談。聲明語句的作用域對應的是一個源代碼的文本區域;它是一個編譯時的屬性。一個變量的生命周期是指程式運作時變量存在的有效時間段,在此時間區域内它可以被程式的其他部分引用;是一個運作時的概念。
2.基礎資料類型
Go語言将資料類型分為四類:基礎類型、複合類型、引用類型和接口類型。
1.整型
- 1、Go語言同時提供了有符号和無符号類型的整數運算。
- 2、有int8、int16、int32和int64四種截 然不同大小的有符号整形數類型,分别對應8、16、32、64bit大小的有符号整形數,與此對 應的是uint8、uint16、uint32和uint64四種無符号整形數類型。
- 3、其中int和int32也是不同的類 型,即使int的大小也是32bit
- 4、運算符按照優先級遞減(二進制運算符有五種優先級。在同一個優先級,使用左優先結合規則,但是使用括号可以明确 優先順序)
- 5、在Go語言中,%取模運算 符的符号和被取模數的符号總是一緻的,是以 -5%3 和 -5%-3 結果都是-2。
- 6、除法運算符 / 的 行為則依賴于操作數是否為全為整數,比如 5.0/4.0 的結果是1.25,但是5/4的結果是1,因為 整數除法會向着0方向截斷餘數。
- 7、算術上,一個 x<<n 左移運算等價于乘以2 ,一個 x>>n 右移運算等價 于除以2 。左移運算用零填充右邊空缺的bit位,無符号數的右移運算也是用0填充左邊空缺的bit位,但是 有符号數的右移運算會用符号位的值填充左邊空缺的bit位。因為這個原因,最好用無符号運算,這樣你可以将整數完全當作一個bit位模式處理。
- 8、無符号數往往隻有在位運算或其它特殊的運算場景才會使用,就像bit集合、 分析二進制檔案格式或者是哈希和加密操作等。它們通常并不用于僅僅是表達非負數量的場 合。
2.浮點數
- 1、float32的有效bit位隻有23個,其它 的bit位用于指數和符号;當整數大于23bit能表達的範圍時,float32的表示将出現誤差
3.複數
- 1、Go語言提供了兩種精度的複數類型:complex64和complex128,分别對應float32和float64兩 種浮點數精度
- 2、内置的complex函數用于建構複數,内建的real和imag函數分别傳回複數的實 部和虛部
4.布爾型
- 1、&&(AND)和||(OR)操作符,可能會有短路行為:如果運算符左邊 值已經可以确定整個布爾表達式的值,那麼運算符右邊的值将不在被求值
5.字元串
1.基本特性
- 1、一個字元串是一個不可改變的位元組序列。
- 2、内置的len函數可以傳回一個字元串中的位元組數目(不是rune字元數目),索引操作s[i]傳回第i 個位元組的位元組值,i必須滿足0 ≤ i< len(s)條件限制。第i個位元組并不一定是字元串的第i個字元,因為對于非ASCII字元的UTF8編碼會要兩個或多個 位元組
- 4、3、子字元串操作s[i:j]基于原始的s字元串的第i個位元組開始到第j個位元組(并不包含j本身)生成一 個新字元串。生成的新字元串将包含j-i個位元組。不管i還是j都可能被忽略,當它們被忽略時将采用0作為開始位置,采用len(s)作為結束的位置。
- 5、字元串可以用==和<進行比較;比較通過逐個位元組比較完成的,是以比較的結果是字元串自然編碼的順序
- 6、字元串的值是不可變的:一個字元串包含的位元組序列永遠不會被改變,追加或者剔除隻會生成新的字元串
- 7、因為字元串是不可修改的,是以嘗試修改字元串内部資料的操作也是被禁止的,比如修改S[0] = ‘i’ 編譯錯誤。
- 8、不變性意味如果兩個字元串共享相同的底層資料的話也是安全的,這使得複制任何長度的字 符串代價是低廉的。同樣,一個字元串s和對應的子字元串切片s[7:]的操作也可以安全地共享相同的記憶體,是以字元串切片操作代價也是低廉的。在這兩種情況下都沒有必要配置設定新的記憶體。
2.字元串和Byte切片
strconv包提供了布爾型、整型數、浮點數和對應字元串的互相轉換,還提供了雙引号轉義相關的轉換。
strings包提供了許多如字元串的查詢、替換、比較、截斷、拆分和合并等功能。
bytes包也提供了很多類似strings功能的函數,因為字元串是隻讀的,是以逐漸建構字元串會導緻很多配置設定和複制。使用bytes.Buffer類型将會更有效
unicode包提供了IsDigit、IsLetter、IsUpper和IsLower等類似功能,它們用于給字元分類
為了避免轉換中不必要的記憶體配置設定,bytes包和strings同時提供了許多實用函數(Strings和Bytes方法名一樣,這邊隻展示一個)。
具體還可參考中文API文檔:API文檔
func Contains(s, substr string) bool //判斷字元串是否包含某個子字元串
func Count(s, sep string) int //判斷子字元串在字元串中出現次數
func Fields(s string) []string //拆分成多個子字元串數組
func HasPrefix(s, prefix string) bool //字元串是否有字首為子字元串
func Index(s, sep string) int //子字元串所在字元串位置
func Join(a []string, sep string) string //往字元串中添加子字元串
3.字元串與數字的轉換
1、将一個整數轉為字元串
- 用fmt.Sprintf傳回一個格式化的字元串:
y := fmt.Sprintf("%d", 123)
- 用strconv.Itoa :
y := strconv.Itoa(123)
FormatInt和FormatUint函數可以用不同的進制來格式化數字:
fmt.Println(strconv.FormatInt(int64(x), 2)) // "1111011"
2、要将一個字元串解析為整數
- strconv包的Atoi(轉成 int 類型):
x, err := strconv.Atoi("123") // x is an int
- strconv包的ParseInt(可以指定位數) :
y, err := strconv.ParseInt("123", 10, 64) // base 10, up to 64 bits
6.常量
1.基本概念
常量表達式的值在編譯期計算,而不是在運作期。每種常量的潛在類型都是基礎類型:boolean、string或數字。
如果是批量聲明的常量,除了第一個外其它的常量右邊的初始化表達式都可以省略,如果省略初始化表達式則表示使用前面常量的初始化表達式寫法,對應的常量類型也一樣的。
const (
a = 1
b
c = 2
d
)
fmt.Println(a, b, c, d) // "1 1 2 2"
2.iota常量生成器
在一個const聲明語句中,在第一個聲明的常量 所在的行,iota将會被置為0,然後在每一個有常量聲明的行加一。
具體可參考:Go語言基礎之變量和常量
防丢失截圖:
3.無類型的常量
- 許多常量并沒有一個明确的基礎類型。分别是無類型的布爾型、無類型的整數、無類型的字元、無類型的浮點數、無類型的複數、無類型的字元串。
- 無類型的常量不僅可以提供更高的運算精度,而且可以直接用于更多的表達式而不需要顯式的類型轉換。
- 隻有常量可以是無類型的。當一個無類型的常量被指派給一個變量的時候,無類型的常量将會被隐式轉換為對應的類型,如果轉換合法的話。
預設類型規則:
- 無類型的整數常量預設轉換為int,對應不确定的記憶體大小
- 浮點數和複數常量則預設轉換為float64和complex128