一、語(yǔ)法結(jié)構(gòu)
golang源碼采用UTF-8編碼??崭癜ǎ嚎瞻?,tab,換行,回車。
- 標(biāo)識(shí)符由字母和數(shù)字組成(外加'_'),字母和數(shù)字都是Unicode編碼。
- 注釋:
復(fù)制代碼 代碼如下:
/* This is a comment; no nesting */
// So is this.
二、字面值(literals)類似C語(yǔ)言中的字面值,但數(shù)值不需要符號(hào)以及大小標(biāo)志:
復(fù)制代碼 代碼如下:
23
0x0FF
1.234e7類似C中的字符串,但字符串是Unicode/UTF-8編碼的。同時(shí),\xNN總是有2個(gè)數(shù)字;\012總是3;兩個(gè)都是字節(jié):
復(fù)制代碼 代碼如下:
"Hello, world\n"
"\xFF" // 1 byte
"\u00FF" // 1 Unicode char, 2 bytes of UTF-8
原生字符串:`\n\.abc\t\` == "\\n\\.abc\\t\\"
三、語(yǔ)法概述
golang基本上就是類C的語(yǔ)法,但使用反轉(zhuǎn)的類型和聲明,并使用關(guān)鍵字作為每個(gè)聲明的開頭。
復(fù)制代碼 代碼如下:
var a int
var b, c *int // 注意與C的不同
var d []int
type S struct { a, b int }
基本的控制結(jié)構(gòu)也十分熟悉:
復(fù)制代碼 代碼如下:
if a == b { return true } else { return false }
for i = 0; i 10; i++ { … }
注意:沒有圓括號(hào),但需要大括號(hào)。
后續(xù)會(huì)有更多有關(guān)這方面的內(nèi)容。
四、分號(hào)
分號(hào)作為語(yǔ)句終止符號(hào),但:
- 如果前一個(gè)符號(hào)是語(yǔ)句的結(jié)尾,那詞法分析程序?qū)⒆詣?dòng)在行尾插入一個(gè)分號(hào)
- 注意:比JavaScript的規(guī)則更清晰和簡(jiǎn)單
因此,下面的程序不需要分號(hào):
復(fù)制代碼 代碼如下:
package main
const three = 3
var i int = three
func main() { fmt.Printf("%d\n", i) }
在實(shí)際中,Go源碼在for和if子句之外幾乎都沒有用到分號(hào)。
五、數(shù)值類型
golang數(shù)值類型(numeric types)是原生內(nèi)置的,也是為大家所熟知的:
復(fù)制代碼 代碼如下:
int uint
int8 uint8 = byte
int16 uint16
int32 uint32 float32 complex64
int64 uint64 float64 complex128
還有uintptr,一個(gè)大小足夠存儲(chǔ)一個(gè)指針的數(shù)值。
這些都是互不相同的類型;int不等于是int32,即便是在一個(gè)32位的機(jī)器上。
沒有隱式類型轉(zhuǎn)換(不過(guò)不要恐慌)。
Bool
普通的布爾類型bool,取值true和false(預(yù)定義的常量)。
if語(yǔ)句等使用布爾表達(dá)式。
指針類型和整型不是布爾類型。
string
原生內(nèi)置的string類型代表不可改變的字節(jié)數(shù)組,即文本。string類型是用長(zhǎng)度定界的,而不是以結(jié)尾0終止的。
字符串字面值是string類型。
和整型一樣不可改變??芍匦沦x值,但不能修改其值。
正如"3"總是3,"hello"也總是"hello"。
Go語(yǔ)言對(duì)字符串操作提供了良好的支持。
六、表達(dá)式(Expressions)
大多都是類C語(yǔ)言的操作符。
二元操作符:
優(yōu)先級(jí) 操作符 備注
5 * / % >> ^ ^是位清理操作符
4 + – | ^ ^是異或(xor)
3 == != = > >=
2
1 ||
一元操作符包括: ! * + – ^(外加用于通信的-)
一元操作符^是求補(bǔ)碼/反碼操作。
Go vs. C表達(dá)式
可以讓C程序員驚喜的是:
更少的優(yōu)先級(jí)層次(應(yīng)該容易)。
^替代了~
++和–不再是表達(dá)式操作符(x++是一個(gè)語(yǔ)句,不是表達(dá)式;*p++是(*p)++,而不是*(p++))
^是新操作符,在常量表達(dá)式中很有用
和>>等需要一個(gè)無(wú)符號(hào)的移位計(jì)數(shù)。
無(wú)驚喜的是:
賦值操作與所期望的一樣:+= = ^=等
表達(dá)式總體看起來(lái)相似(下標(biāo)、函數(shù)調(diào)用等)
例子:
復(fù)制代碼 代碼如下:
+x
23 + 3*x[i]
x = f()
^a >> b
f() || g()
x == y + 1 -ch > 0
x ^ 7 // x with the low 3 bits cleared
fmt.Printf("%5.2g\n", 2*math.Sin(PI/8))
7.234/x + 2.3i
"hello, " + "world" // concatenation
// no C-like "a" "b"
數(shù)值轉(zhuǎn)型
將一個(gè)數(shù)值從一個(gè)類型轉(zhuǎn)換為另一個(gè)類型稱為一次轉(zhuǎn)型,其語(yǔ)法形式有點(diǎn)類似函數(shù)調(diào)用:
復(fù)制代碼 代碼如下:
uint8(intVar) //截?cái)嗟较鄳?yīng)的大小
int(float64Var) //片段截?cái)?br />
float64(intVar) //轉(zhuǎn)為float64
一些涉及string類型的轉(zhuǎn)型:
復(fù)制代碼 代碼如下:
string(0×1234) // == "\u1234"
string(sliceOfBytes) // bytes -> bytes
string(sliceOfInts) // ints -> Unicode/UTF-8
[]byte("abc") // bytes -> bytes
[]int("日本語(yǔ)") // Unicode/UTF-8 -> ints
切片(slice)與數(shù)組相關(guān),稍后會(huì)有更多相關(guān)內(nèi)容。
七、常量
數(shù)值常量是"理想數(shù)":沒有大小或標(biāo)志,因此沒有U、L或UL作結(jié)尾。
復(fù)制代碼 代碼如下:
077 // 八進(jìn)制
0xFEEDBEEEEEEEEEEEEEEEEEEEEF //十六進(jìn)制
1 100
下面是整數(shù)和浮點(diǎn)數(shù)值,字面值的語(yǔ)法決定其類型:
復(fù)制代碼 代碼如下:
1.234e5 // 浮點(diǎn)
1e2 // 浮點(diǎn)
3.2i // 浮點(diǎn)虛數(shù)
100 // 整數(shù)
常量表達(dá)式
浮點(diǎn)和整型常量可以任意組合,最終表達(dá)式的類型由常量的類型決定。操作自身也取決于類型。
復(fù)制代碼 代碼如下:
2*3.14 // 浮點(diǎn): 6.28
3./2 // 浮點(diǎn):1.5
3/2 // 整型:1
3+2i // 復(fù)數(shù):3.0 + 2.0i
// 高精度
const Ln2 = 0.69314718055994530941723212145817656807
const Log2E = 1/Ln2
數(shù)值的表示范圍足夠大(目前最大用1024位表示)。
理想數(shù)的結(jié)果
Go語(yǔ)言允許無(wú)需顯式轉(zhuǎn)型的情況下使用常量,前提是常量值可以被其類型表示(沒有必要進(jìn)行轉(zhuǎn)型;其值表示起來(lái)沒問(wèn)題):
復(fù)制代碼 代碼如下:
var million int = 1e6 //float語(yǔ)法在這里可以使用
math.Sin(1)
常量必須可以被其類新表示。例如:^0的值為-1,不在0-255的范圍內(nèi)。
復(fù)制代碼 代碼如下:
uint8(^0) //錯(cuò)誤:-1無(wú)法用uint8類型表示
^uint8(0) //OK
uint8(350) //錯(cuò)誤:350無(wú)法用uint8類型表示
uint8(35.0) //OK: 35
uint8(3.5) //錯(cuò)誤:3.5無(wú)法用uint8類型表示
八、聲明
golang聲明以一個(gè)關(guān)鍵字開頭(var, const,type和func),并且與C中的聲明次序相反:
復(fù)制代碼 代碼如下:
var i int
const PI = 22./7.
type Point struct { x, y int }
func sum(a, b int) int { return a + b }
為何要以相反次序聲明呢?早期的一個(gè)例子:
復(fù)制代碼 代碼如下:
var p, q *int
p和q的類型都是*int。并且函數(shù)讀起來(lái)更佳,并且與其他聲明一致。還有一個(gè)原因,馬上道來(lái)。
Var
變量聲明以var開頭。
它們可以有一個(gè)類型或一個(gè)初始化表達(dá)式;至少應(yīng)有一個(gè)或二者都有。初始化表達(dá)式應(yīng)該與變量匹配(還有類型!)。
復(fù)制代碼 代碼如下:
var i int
var j = 365.245
var k int = 0
var l, m uint64 = 1, 2
var nanoseconds int64 = 1e9 // float64 constant!
var inter, floater, stringer = 1, 2.0, "hi"
分派var
總是輸入var讓人生厭。我們可以通過(guò)括號(hào)讓多個(gè)變量聲明成為一組:
復(fù)制代碼 代碼如下:
var (
i int
j = 356.245
k int = 0
l, m uint64 = 1, 2
nanoseconds int64 = 1e9
inter, floater, stringer = 1, 2.0, "hi"
)
這種形式適用于const,type, var,但不能用于func。
=:"短聲明"
在函數(shù)內(nèi)(只有在函數(shù)內(nèi)這一種情況下),下面形式的聲明:
復(fù)制代碼 代碼如下:
var v = value
可以被縮短成:
復(fù)制代碼 代碼如下:
v := value
(這就是另外一個(gè)名字、類型倒序的原因)
類型就是值的類型(對(duì)于理想數(shù),相應(yīng)的類型是int或float64或complex128)
復(fù)制代碼 代碼如下:
a, b, c, d, e := 1, 2.0, "three", FOUR, 5e0i
這種形式的聲明使用很頻繁,并且在諸如for循環(huán)初始化表達(dá)式中也可以使用。
Const
常量聲明以const開頭。
它們必須有一個(gè)常量表達(dá)式,可在編譯期間求值,作為初始化表達(dá)式,可以擁有一個(gè)可選的類型修飾符。
復(fù)制代碼 代碼如下:
const Pi = 22./7.
const AccuratePi float64 = 355./113
const beef, two, parsnip = "meat", 2, "veg"
const (
Monday, Tuesday, Wednesday = 1, 2, 3
Thursday, Friday, Saturday = 4, 5, 6
)
Iota
常量聲明可以使用計(jì)數(shù)器:iota,每個(gè)const塊中的iota都從0開始計(jì)數(shù),在每個(gè)隱式的分號(hào)(行尾)自增。
復(fù)制代碼 代碼如下:
const (
Monday = iota // 0
Tuesday = iota // 1
)
速記:重復(fù)上一個(gè)類型和表達(dá)式。
復(fù)制代碼 代碼如下:
const (
loc0, bit0 uint32 = iota, 1iota //0,1
loc1, bit1 //1,2
loc2, bit2 //2,4
)
Type
類型聲明以type開頭。
我們后續(xù)會(huì)學(xué)習(xí)更多類型,不過(guò)先這里舉幾個(gè)例子:
復(fù)制代碼 代碼如下:
type Point struct {
x, y, z float64
name
string
}
type Operator func(a, b int) int
type SliceOfIntPointers []*int
我們稍后會(huì)回到函數(shù)。
New
內(nèi)置函數(shù)new用于分配內(nèi)存。其語(yǔ)法類似一個(gè)函數(shù)調(diào)用,以類型作為參數(shù),與C++中的new類似。返回一個(gè)指向已分配對(duì)象的指針。
復(fù)制代碼 代碼如下:
var p *Point = new(Point)
v := new(int) // v的類型為*int
稍后我們將看到如何構(gòu)建切片(slice)
Go語(yǔ)言中沒有用于內(nèi)存釋放的delete或free。Go具備垃圾回收功能。
賦值
賦值是容易和熟悉的:
復(fù)制代碼 代碼如下:
a = b
但Go還支持多項(xiàng)賦值:
復(fù)制代碼 代碼如下:
x, y, z = f1(), f2(), f3()
a, b = b, a //交互a,b的值
函數(shù)支持多個(gè)返回值(稍后有更多細(xì)節(jié)):
復(fù)制代碼 代碼如下:
nbytes, error := Write(buf)
九、控制結(jié)構(gòu)
與C類似,但很多地方有不同。
Go支持if、for和switch。
正如之前說(shuō)的,無(wú)需小括號(hào),但大括號(hào)是必要的。
如果將它們看為一組,它們的用法很規(guī)律。例如,if、for和switch都支持初始化語(yǔ)句。
控制結(jié)構(gòu)的形式
后續(xù)會(huì)有細(xì)節(jié),但總體上:
if和switch語(yǔ)句以1元素和2元素形式呈現(xiàn),后面詳細(xì)講解。
for循環(huán)具有1元素和3元素的形式:
1元素形式等價(jià)于C語(yǔ)言中的while:
復(fù)制代碼 代碼如下:
for a {}
3元素形式等價(jià)于C語(yǔ)言中的for:
復(fù)制代碼 代碼如下:
for a;b;c {}
在所有這些形式里,任何元素都可以是空。
if
基本形式是大家所熟知的,但已經(jīng)沒有了"else懸掛"問(wèn)題了:
復(fù)制代碼 代碼如下:
if x 5 { less() }
if x 5 { less() } else if x == 5 { equal() }
支持初始化語(yǔ)句;需要分號(hào)。
復(fù)制代碼 代碼如下:
if v := f(); v 10 {
fmt.Printf("%d less than 10\n", v)
} else {
fmt.Printf("%d not less than 10\n", v)
}
與多元函數(shù)一起使用更有益處:
復(fù)制代碼 代碼如下:
if n, err = fd.Write(buf); err != nil { … }
省略條件意為true,在這里沒有什么用。但在for,switch語(yǔ)句中尤其有用。
for
基本形式是大家所熟知的:
復(fù)制代碼 代碼如下:
for i := 0; i 10; i++ { … }
省略條件意為true:
復(fù)制代碼 代碼如下:
for ;; { fmt.Printf("looping forever") }
而且你還可以省略分號(hào):
復(fù)制代碼 代碼如下:
for { fmt.Printf("Mine! ") }
不要忘記多項(xiàng)賦值:
復(fù)制代碼 代碼如下:
for i,j := 0,N; i j; i,j = i+1,j-1 {…}
(Go中沒有像C中那樣的逗號(hào)操作符)
switch細(xì)節(jié)
switch與C中的switch有些類似。
不過(guò),有一些語(yǔ)法和語(yǔ)義的重要不同之處:
- 表達(dá)式不必一定是常量,甚至可以不必是int。
- 沒有自動(dòng)的fall through
- 但作為替代,語(yǔ)法上,最后的語(yǔ)句可以為fallthrough
- 多case可以用逗號(hào)分隔
復(fù)制代碼 代碼如下:
switch count%7 {
case 4,5,6: error()
case 3: a *= v; fallthrough
case 2: a *= v; fallthrough
case 1: a *= v; fallthrough
case 0: return a*v
}
Switch
Go中的switch要遠(yuǎn)比C中的強(qiáng)大。常見的形式:
復(fù)制代碼 代碼如下:
switch a {
case 0: fmt.Printf("0")
default: fmt.Printf("non-zero")
}
switch表達(dá)式可以是任意類型,如果為空,則表示true。結(jié)果類似一個(gè)if-else鏈:
復(fù)制代碼 代碼如下:
a, b := x[i], y[j]
switch {
case a b: return -1
case a == b: return 0
case a > b: return 1
}
或
復(fù)制代碼 代碼如下:
switch a, b := x[i], y[j]; { … }
Break,continue等
break和continue語(yǔ)句的工作方式與C中的類似。
它們可以指定一個(gè)label并影響外層結(jié)構(gòu):
復(fù)制代碼 代碼如下:
Loop: for i := 0; i 10; i++ {
switch f(i) {
case 0, 1, 2: break Loop
}
g(i)
}
是的,那是一個(gè)goto。
十、函數(shù)
函數(shù)以func關(guān)鍵字開頭。
如果有返回類型,返回類型放在參數(shù)的后面。return的含義和你期望的一致。
復(fù)制代碼 代碼如下:
func square(f float64) float64 { return f*f }
函數(shù)支持返回多個(gè)值。這樣,返回類型就是一個(gè)括號(hào)包圍的列表。
復(fù)制代碼 代碼如下:
func MySqrt(f float64) (float64, bool) {
if f >= 0 { return math.Sqrt(f), true }
return 0, false
}
空標(biāo)識(shí)符
如果你只關(guān)心MySqrt函數(shù)返回的第一個(gè)值?你仍然需要將第二個(gè)值放在一個(gè)地方。
解決方法:使用空標(biāo)識(shí)符_(下劃線)。它是預(yù)聲明的,可以被賦予任何無(wú)用的值。
復(fù)制代碼 代碼如下:
// Don't care about boolean from MySqrt.
val, _ = MySqrt(foo())
在空標(biāo)識(shí)符其他的適用場(chǎng)合中,我們?nèi)匀粫?huì)展示它。
帶結(jié)果變量(result variable)的函數(shù)
如果你給結(jié)果參數(shù)命名了,你可以將它當(dāng)作實(shí)際變量使用。
復(fù)制代碼 代碼如下:
func MySqrt(f float64) (v float64, ok bool) {
if f >= 0 { v,ok = math.Sqrt(f), true }
else { v,ok = 0,false }
return v,ok
}
結(jié)果變量被初始化為"0"(0,0.0,false等。根據(jù)其類型;稍后有更多有關(guān)內(nèi)容)
復(fù)制代碼 代碼如下:
func MySqrt(f float64) (v float64, ok bool) {
if f >= 0 { v,ok = math.Sqrt(f), true }
return v,ok
}
空返回
最后,一個(gè)沒有返回表達(dá)式的return將返回結(jié)果變量的當(dāng)前值。下面是另外兩個(gè)MySqrt的版本:
復(fù)制代碼 代碼如下:
func MySqrt(f float64) (v float64, ok bool) {
if f >= 0 { v,ok = math.Sqrt(f), true }
return // must be explicit
}
func MySqrt(f float64) (v float64, ok bool) {
if f 0 { return } // error case
return math.Sqrt(f),true
}
0是什么
Go中的內(nèi)存都是被初始化了的。所有變量在執(zhí)行之前的聲明時(shí)被初始化。如果沒有顯式的初始化表達(dá)式,我們將使用對(duì)應(yīng)類型的"0值"。下面的循環(huán):
復(fù)制代碼 代碼如下:
for i := 0; i 5; i++ {
var v int
fmt.Printf("%d ", v)
v = 5
}
將打印0 0 0 0 0。
0值取決于類型:數(shù)值是0;布爾是false;空字符串是"";指針,map、切片、channel是nil;結(jié)構(gòu)體是0等。
Defer
defer語(yǔ)句負(fù)責(zé)在其所在的函數(shù)返回時(shí)執(zhí)行一個(gè)函數(shù)(或方法)。其參數(shù)在到達(dá)defer語(yǔ)句那個(gè)時(shí)刻被求值;其函數(shù)在返回時(shí)被執(zhí)行。
復(fù)制代碼 代碼如下:
func data(fileName string) string {
f := os.Open(fileName)
defer f.Close()
contents := io.ReadAll(f)
return contents
}
在關(guān)閉文件描述符、解互斥鎖等場(chǎng)合十分有用。
每Defer執(zhí)行一個(gè)函數(shù)
Go按按后入先出(LIFO)次序執(zhí)行一組defer函數(shù)。
復(fù)制代碼 代碼如下:
func f() {
for i := 0; i 5; i++ {
defer fmt.Printf("%d ", i)
}
}
上面代碼將輸出4 3 2 1 0。你可以在最后關(guān)閉所有文件描述符以及解鎖所有互斥鎖。
用defer跟蹤代碼:
復(fù)制代碼 代碼如下:
func trace(s string) { fmt.Println("entering:", s) }
func untrace(s string) { fmt.Println("leaving:", s) }
func a() {
trace("a")
defer untrace("a")
fmt.Println("in a")
}
func b() {
trace("b")
defer untrace("b")
fmt.Println("in b")
a()
}
func main() { b() }
不過(guò)我們可以實(shí)現(xiàn)的更靈巧一些。
參數(shù)當(dāng)即求值,defer稍后執(zhí)行
復(fù)制代碼 代碼如下:
func trace(s string) string {
fmt.Println("entering:", s)
return s
}
func un(s string) {
fmt.Println("leaving:", s)
}
func a() {
defer un(trace("a"))
fmt.Println("in a")
}
func b() {
defer un(trace("b"))
fmt.Println("in b")
a()
}
func main() { b() }
函數(shù)字面值
和在C中一樣,函數(shù)不能在函數(shù)內(nèi)部聲明。但函數(shù)字面值卻可以被賦值給變量。
復(fù)制代碼 代碼如下:
func f() {
for i := 0; i 10; i++ {
g := func(i int) { fmt.Printf("%d",i) }
g(i)
}
}
函數(shù)字面值是閉包(closure)
函數(shù)字面值實(shí)際上是閉包。
復(fù)制代碼 代碼如下:
func adder() (func(int) int) {
var x int
return func(delta int) int {
x += delta
return x
}
}
f := adder()
fmt.Print(f(1))
fmt.Print(f(20))
fmt.Print(f(300))
輸出1 21 321 – f中的x累加。
您可能感興趣的文章:- 模塊一 GO語(yǔ)言基礎(chǔ)知識(shí)-庫(kù)源碼文件
- GO語(yǔ)言(golang)基礎(chǔ)知識(shí)
- Go語(yǔ)言基礎(chǔ)知識(shí)點(diǎn)介紹