最近在學習golang,寫下學習筆記提高記憶。
爲了看起來不是那麼枯燥,本學習筆記採用分析代碼的形式。html
首先搬出咱們最經典的第一段代碼:java
package main // 0 import "fmt" // 1實現格式化的 I/O /* Print something */ // 2 func main() { // 3 fmt.Println("Hello, world; or καλημε ́ρα κóσμε; orこんにちは 世界") // 4 }
首先咱們要認識到python
每一個Go 程序都是由包組成,程序的運行入口是包main
構建 Go 程序的最佳途徑是使用 go 工具。 構建 helloworld 只須要:git
1. go build helloworld.go # 結果是叫作 helloworld 的可執行文件。 2. ./helloworld # Hello, world; or καλημε ́ρα κóσμε; or こんにちは世界
Go 是靜態類型語言,不能在運行期改變變量類型。github
變量若是不提供初始化值將自動初始化爲零值。若是提供初始化值,可省略變量類型,由編譯器自動推斷。golang
var x int // 使用關鍵字 var 定義變量, 跟函數的參數列表同樣,類型在後面。 var c, python, java bool // 多個相同類型的變量能夠寫在一行。 var f float32 = 1.6 var i, j int = 1, 2 // 變量定義能夠包含初始值,每一個變量對應一個。 var s = "abc" // 若是初始化是使用表達式,則能夠省略類型;變量從初始值中得到類型。
變量在定義時沒有明確的初始化時會賦值爲零值 。小程序
零值是:設計模式
在函數內部,可用更簡略的 ":=" 式定義變量。數組
func main() { n, s := 12, "Hello, World!" println(s, n) }
函數外的每一個語句都必須以關鍵字開始( var 、 func 、等等), := 結構不能使用在函數外。
可一次定義多個變量。數據結構
var x, y, z int var s, n = "abc", 123 var ( a int b float32 ) func main() { n, s := 0x1234, "Hello, World!" println(x, s, n) }
一個特殊的變量名是 _(下劃線)。任何賦給它的值都被丟棄。在這個例子中,將 35 賦值給 b,同時丟棄 34。
_, b := 34, 35
Go 的編譯器會對聲明卻未使用的變量報錯
var s string // 全局變量沒問題。 func main() { i := 0 // Error: i declared and not used。(可以使 "_ = i" 規避) }
定義完以後的變量能夠被從新賦值 好比第8行,將計算結果賦值給result。
常量值必須是編譯期可肯定的數字、字符串、布爾值。
常量的定義與變量相似,只不過使用 const 關鍵字
const x, y int = 1, 2 const s = "Hello, World!" // 多常量初始化 // 類型推斷 // 常量組 const ( a, b = 10, 100 c bool = false ) func main() _{ const x = 'xxx' // 未使用局部常量不會引起編譯錯誤 }
在常量中,若是不提供類型和初始化值,那麼被看做和上一常量相同
const ( s = "abc" x // x = "abc" )
一般狀況下 go 語言的變量持有相應的值
。
對於通道
、函數
、方法
、映射
以及切片
的引用變量,它們持有的都是引用
,也既是保存指針的變量
。
值在傳遞給函數或者方法的時候會被複制一次
不一樣類型參數所佔空間以下:
類型 | 佔用空間 |
---|---|
bool | 類型佔1~8個字節 |
傳遞字符串 | 佔 16個字節(64位)或者8個字節(32位) |
傳遞切片 | 佔 16個字節(64位)或者12個字節(32位) |
傳遞指針 | 佔 8個字節(64位)或者4個字節(32位) |
數組
是按值傳遞的,因此傳遞大數組代價較大 可用切片代替
變量是賦給內存塊的名字,該內存塊用於保存特定的數據類型
。
指針是指保存了另外一個變量內存地址的變量
。建立的指針用來指向另外一個某種類型的變量。
爲了便於理解,咱們看如下兩段代碼。
x := 3 y := 22 // 變量 x, y 爲int型 分別賦值 3 22 內存地址 0xf840000148 0xf840000150 x == 3 && y == 22
pi := &x // 變量pi 爲 *int(指向int型變量的指針) 在這裏咱們將變量x的內存地址賦值給pi,即pi 保存了另外一個變量的內存地址(這也是指針定義) pi == 3 && x == 3 && y == 22 x++ // x + 1 此時 x==4 pi 指向x的內存地址 因此 pi == 4 && x == 4 && y == 22 *pi++ // *pi ++ 意爲着pi指向的值增長 *pi == 5 & x == 5 && y == 22 pi := &y //pi 指向y的內存地址 *pi == 22 && x == 5 && y == 22 *pi++ // *pi++ 意爲着pi指向的值增長 *pi == 23 && x == 5 && y == 23
Go 有明確的數字類型命名, 支持 Unicode, 支持經常使用數據結構
類型 | 長度 | 默認值 | 說明 |
---|---|---|---|
bool | 1 | false | |
byte | 1 | 0 | unit8 |
rune | 4 | 0 | int32 的別名 表明一個Unicode 碼 |
int, unit | 4 或 8 | 0 | 32 或 64 |
int8, unit8 | 1 | 0 | -128 ~ 127, 0~255 |
int16, unit16 | 2 | 0 | -32768 ~ 32767, 0 ~ 65535 |
int32, unit32 | 4 | 0 | -21億~ 21億, 0 ~ 42億 |
int64, unit64 | 8 | 0 | |
float32 | 4 | 0.0 | |
float64 | 8 | 0.0 | |
complex64 | 8 | ||
complex128 | 16 | ||
unitptr | 4或8 | 足以存儲指針的unit32 或unit64 整數 | |
array | 值類型 | ||
struct | 值類型 | ||
string | "" | UTF-8 字符串 | |
slice | nil | 引用類型 | |
map | nil | 引用類型 | |
channel | nil | 引用類型 | |
interface | nil | 接口 | |
function | nil | 函數 |
int
,uint
和uintptr
類型在32位的系統上通常是32位,而在64位系統上是64位。當你須要使用一個整數類型時,你應該首選int
,僅當有特別的理由才使用定長整數類型或者無符號整數類型。
引用類型包括slice
、map
和channel
。它們有複雜的內部結構,除了申請內存外,還須要初始化相關屬性
go 不支持
隱式的類型轉換
使用表達式 T(v) 將值 v 轉換爲類型 T 。
var b byte = 100 // var n int = b // Error: cannot use b (type byte) as type int in assignment var n int = int(b) // 顯式轉換
不能將其餘類型當 bool 值使用
a := 100 if a { // Error: non-bool a (type int) used as if condition println("true") }
首先看下面這段代碼
package main import "fmt" func add(x int, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) }
使用關鍵字 func 定義函數,左大括號不能另起一行
golang中符合規範的函數通常寫成以下的形式:
func functionName(parameter_list) (return_value_list) { … } // parameter_list 是參數列表 // return_value_list 是返回值列表 下邊有詳細的講解
func test(x int, y int, s string) (r int, s string) { // 類型相同的相鄰參數可合併 n := x + y // 多返回值必須用括號。 return n, fmt.Sprintf(s, n) }
關鍵字func
用於定義一個函數
test
是你函數的名字
int 類型的變量 x, y 和 string 類型的變量 s 做爲輸入參數
參數用pass-by-value
方式傳遞,意味着它們會被複制
當兩個或多個連續的函數命名參數是同一類型
,則除了最後一個類型以外,其餘均可以省略。
在這個例子中:
x int, y int
被縮寫爲
x, y int
變量
r 和 s 是這個函數的命名返回值
。在 Go 的函數中能夠返回多個值。
若是不想對返回的參數命名,只須要提供類型:(int, string)。 若是只有一個返回值
,能夠省略圓括號。若是函數是一個子過程,而且沒有任何返回值,也能夠省略這些內容。
函數體。注意 return 是一個語句,因此包裹參數的括號是可選的。
不定長參數其實就是slice,只能有一個,且必須是最後一個。
func test(s string, n ...int) string { var x int for _, i := range n { x += i } return fmt.Sprintf(s, x) } // 使用slice 作變參時,必須展開 func main() { s := []int{1, 2, 3} println(test("sum: %d", s...)) }
函數是第一類對象,可做爲參數傳遞
就像其餘在 Go 中的其餘東西同樣,函數也是值而已。它們能夠像下面這樣賦值給變量:
func main() { a := func() { // 定義一個匿名函數,而且賦值給 a println("Hello") } // 這裏沒有 () a() // 調用函數 }
若是使用 fmt.Printf("%Tn", a) 打印 a 的類型,輸出結果是 func()
函數能夠返回任意數量返回值
Go 函數的返回值或者結果參數能夠指定一個名字,而且像原始的變量那樣使用,就像 輸入參數那樣。若是對其命名,在函數開始時,它們會用其類型的零值初始化
package main import "fmt" func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap("hello", "world") fmt.Println(a, b) } /* 函數能夠返回任意數量返回值 swap 函數返回了兩個字符串 */
Go 的返回值能夠被命名,而且就像在函數體開頭聲明的變量那樣使用。
package main import "fmt" func split(sum int) (x, y int) { // 初始化返回值爲 x,y x = sum * 4 / 9 // x,y 已經初始化,能夠直接賦值使用 y = sum - x return // 隱式返回x,y(裸返回) } func main() { fmt.Println(split(17)) } /* 在長的函數中這樣的裸返回會影響代碼的可讀性。 */
有返回值的函數,必須有明確的return 語句,不然會引起編譯錯誤
函數原型
函數聲明
由函數返回類型、函數名和形參列表組成。形參列表必須包括形參類型,可是沒必要對形參命名。這三個元素被稱爲函數原型,函數原型描述了函數的接口
函數原型
相似函數定義時的函數頭,又稱函數聲明。爲了能使函數在定義以前就能被調用,C++規定能夠先說明函數原型,而後就能夠調用函數。函數定義可放在程序後面。 因爲函數原型是一條語句,所以函數原型必須以分號結束。函數原型由函數返回類型、函數名和參數表組成,它與函數定義的返回類型、函數名和參數表必須一致。函數原型必須包含參數的標識符(對函數聲明而言是可選的)
注意:函數原型與函數定義
必須一致,不然會引發鏈接錯誤。
變量和函數部分暫時這些,有更新還會補充。下一篇將會是控制流。
將會用到的代碼爲:
package main import "fmt" func main() { result := 0 for i := 0; i <= 10; i++ { result = fibonacci(i) fmt.Printf("fibonacci(%d) is: %d\n", i, result) } } func fibonacci(n int) (res int) { if n <= 1 { res = 1 } else { res = fibonacci(n-1) + fibonacci(n-2) } return }
最後,感謝女友支持和包容,比❤️
想了解如下內容能夠在公號輸入相應關鍵字獲取歷史文章: 公號&小程序
| 設計模式
| 併發&協程