Go語言基礎知識總結(語法、變數、數值型別、表示式、控制結構等)

NO IMAGE

一、語法結構

golang原始碼採用UTF-8編碼。空格包括:空白,tab,換行,回車。

– 識別符號由字母和數字組成(外加’_’),字母和數字都是Unicode編碼。

– 註釋:
複製程式碼 程式碼如下:
/* This is a comment; no nesting */
// So is this.

二、字面值(literals)類似C語言中的字面值,但數值不需要符號以及大小標誌:

複製程式碼 程式碼如下:
23
0x0FF

1.234e7類似C中的字串,但字串是Unicode/UTF-8編碼的。同時,\xNN總是有2個數字;\012總是3;兩個都是位元組:

複製程式碼 程式碼如下:”Hello, world\n”
“\xFF” // 1 byte
“\u00FF” // 1 Unicode char, 2 bytes of UTF-8

原生字串:`\n\.abc\t\` == “\\n\\.abc\\t\\”

三、語法概述

golang基本上就是類C的語法,但使用反轉的型別和宣告,並使用關鍵字作為每個宣告的開頭。

複製程式碼 程式碼如下:
var a int
var b, c *int // 注意與C的不同
var d []int
type S struct { a, b int }

基本的控制結構也十分熟悉:

複製程式碼 程式碼如下:
if a == b { return true } else { return false }
for i = 0; i < 10; i { … }

注意:沒有圓括號,但需要大括號。

後續會有更多有關這方面的內容。

四、分號

分號作為語句終止符號,但:

– 如果前一個符號是語句的結尾,那詞法分析程式將自動在行尾插入一個分號
– 注意:比JavaScript的規則更清晰和簡單

因此,下面的程式不需要分號:

複製程式碼 程式碼如下:
package main

const three = 3
var i int = three

func main() { fmt.Printf(“%d\n”, i) }

在實際中,Go原始碼在for和if子句之外幾乎都沒有用到分號。

五、數值型別

golang數值型別(numeric types)是原生內建的,也是為大家所熟知的:

複製程式碼 程式碼如下:
int uint
int8 uint8 = byte
int16 uint16
int32 uint32 float32 complex64
int64 uint64 float64 complex128

還有uintptr,一個大小足夠儲存一個指標的數值。

這些都是互不相同的型別;int不等於是int32,即便是在一個32位的機器上。

沒有隱式型別轉換(不過不要恐慌)。

Bool

普通的布林型別bool,取值true和false(預定義的常量)。

if語句等使用布林表示式。

指標型別和整型不是布林型別。

string

原生內建的string型別代表不可改變的位元組陣列,即文字。string型別是用長度定界的,而不是以結尾0終止的。

字串字面值是string型別。

和整型一樣不可改變。可重新賦值,但不能修改其值。

正如”3″總是3,”hello”也總是”hello”。

Go語言對字串操作提供了良好的支援。

六、表示式(Expressions)

大多都是類C語言的操作符。

二元操作符:

優先順序 操作符 備註
5 * / % << >> & &^ &^是位清理操作符
4 – | ^ ^是異或(xor)
3 == != < <= > >=
2 &&
1 ||

一元操作符包括:& ! * – ^(外加用於通訊的<-)
一元操作符^是求補碼/反碼操作。

Go vs. C表示式

可以讓C程式設計師驚喜的是:

更少的優先順序層次(應該容易)。
^替代了~
和–不再是表示式操作符(x 是一個語句,不是表示式;*p 是(*p) ,而不是*(p ))
&^是新操作符,在常量表示式中很有用
<<和>>等需要一個無符號的移位計數。

無驚喜的是:

賦值操作與所期望的一樣: = <<= &^=等
表示式總體看起來相似(下標、函式呼叫等)

例子:
複製程式碼 程式碼如下:
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”

數值轉型

將一個數值從一個型別轉換為另一個型別稱為一次轉型,其語法形式有點類似函式呼叫:
複製程式碼 程式碼如下:
uint8(intVar) //截斷到相應的大小
int(float64Var) //片段截斷
float64(intVar) //轉為float64

一些涉及string型別的轉型:
複製程式碼 程式碼如下:
string(0×1234) // == “\u1234”
string(sliceOfBytes) // bytes -> bytes
string(sliceOfInts) // ints -> Unicode/UTF-8
[]byte(“abc”) // bytes -> bytes
[]int(“日本語”) // Unicode/UTF-8 -> ints

切片(slice)與陣列相關,稍後會有更多相關內容。

七、常量

數值常量是”理想數”:沒有大小或標誌,因此沒有U、L或UL作結尾。
複製程式碼 程式碼如下:
077 // 八進位制
0xFEEDBEEEEEEEEEEEEEEEEEEEEF //十六進位制
1 << 100

下面是整數和浮點數值,字面值的語法決定其型別:
複製程式碼 程式碼如下:
1.234e5 // 浮點
1e2 // 浮點
3.2i // 浮點虛數
100 // 整數

常量表示式

浮點和整型常量可以任意組合,最終表示式的型別由常量的型別決定。操作自身也取決於型別。
複製程式碼 程式碼如下:
2*3.14 // 浮點: 6.28
3./2 // 浮點:1.5
3/2 // 整型:1
3 2i // 複數:3.0 2.0i

// 高精度
const Ln2 = 0.69314718055994530941723212145817656807
const Log2E = 1/Ln2

數值的表示範圍足夠大(目前最大用1024位表示)。

理想數的結果

Go語言允許無需顯式轉型的情況下使用常量,前提是常量值可以被其型別表示(沒有必要進行轉型;其值表示起來沒問題):
複製程式碼 程式碼如下:
var million int = 1e6 //float語法在這裡可以使用
math.Sin(1)

常量必須可以被其類新表示。例如:^0的值為-1,不在0-255的範圍內。
複製程式碼 程式碼如下:
uint8(^0) //錯誤:-1無法用uint8型別表示
^uint8(0) //OK
uint8(350) //錯誤:350無法用uint8型別表示
uint8(35.0) //OK: 35
uint8(3.5) //錯誤:3.5無法用uint8型別表示

八、宣告

golang宣告以一個關鍵字開頭(var, const,type和func),並且與C中的宣告次序相反:
複製程式碼 程式碼如下:
var i int
const PI = 22./7.
type Point struct { x, y int }
func sum(a, b int) int { return a b }

為何要以相反次序宣告呢?早期的一個例子:
複製程式碼 程式碼如下:
var p, q *int

p和q的型別都是*int。並且函式讀起來更佳,並且與其他宣告一致。還有一個原因,馬上道來。

Var

變數宣告以var開頭。

它們可以有一個型別或一個初始化表示式;至少應有一個或二者都有。初始化表示式應該與變數匹配(還有型別!)。
複製程式碼 程式碼如下:
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讓人生厭。我們可以通過括號讓多個變數宣告成為一組:
複製程式碼 程式碼如下:
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。

=:”短宣告”

在函式內(只有在函式內這一種情況下),下面形式的宣告:
複製程式碼 程式碼如下:
var v = value

可以被縮短成:
複製程式碼 程式碼如下:
v := value

(這就是另外一個名字、型別倒序的原因)

型別就是值的型別(對於理想數,相應的型別是int或float64或complex128)
複製程式碼 程式碼如下:
a, b, c, d, e := 1, 2.0, “three”, FOUR, 5e0i

這種形式的宣告使用很頻繁,並且在諸如for迴圈初始化表示式中也可以使用。

Const

常量宣告以const開頭。

它們必須有一個常量表示式,可在編譯期間求值,作為初始化表示式,可以擁有一個可選的型別修飾符。
複製程式碼 程式碼如下:
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

常量宣告可以使用計數器:iota,每個const塊中的iota都從0開始計數,在每個隱式的分號(行尾)自增。
複製程式碼 程式碼如下:
const (
Monday = iota // 0
Tuesday = iota // 1
)

速記:重複上一個型別和表示式。
複製程式碼 程式碼如下:
const (
loc0, bit0 uint32 = iota, 1<<iota //0,1
loc1, bit1 //1,2
loc2, bit2 //2,4
)

Type

型別宣告以type開頭。

我們後續會學習更多型別,不過先這裡舉幾個例子:
複製程式碼 程式碼如下:
type Point struct {
x, y, z float64
name
string
}
type Operator func(a, b int) int
type SliceOfIntPointers []*int

我們稍後會回到函式。

New

內建函式new用於分配記憶體。其語法類似一個函式呼叫,以型別作為引數,與C 中的new類似。返回一個指向已分配物件的指標。
複製程式碼 程式碼如下:
var p *Point = new(Point)
v := new(int) // v的型別為*int

稍後我們將看到如何構建切片(slice)

Go語言中沒有用於記憶體釋放的delete或free。Go具備垃圾回收功能。

賦值

賦值是容易和熟悉的:
複製程式碼 程式碼如下:
a = b

但Go還支援多項賦值:
複製程式碼 程式碼如下:
x, y, z = f1(), f2(), f3()
a, b = b, a //互動a,b的值

函式支援多個返回值(稍後有更多細節):
複製程式碼 程式碼如下:
nbytes, error := Write(buf)

九、控制結構

與C類似,但很多地方有不同。

Go支援if、for和switch。

正如之前說的,無需小括號,但大括號是必要的。

如果將它們看為一組,它們的用法很規律。例如,if、for和switch都支援初始化語句。

控制結構的形式

後續會有細節,但總體上:

if和switch語句以1元素和2元素形式呈現,後面詳細講解。

for迴圈具有1元素和3元素的形式:

1元素形式等價於C語言中的while:

複製程式碼 程式碼如下:
for a {}

3元素形式等價於C語言中的for:

複製程式碼 程式碼如下:
for a;b;c {}

在所有這些形式裡,任何元素都可以是空。

if

基本形式是大家所熟知的,但已經沒有了”else懸掛”問題了:

複製程式碼 程式碼如下:
if x < 5 { less() }
if x < 5 { less() } else if x == 5 { equal() }

支援初始化語句;需要分號。

複製程式碼 程式碼如下:
if v := f(); v < 10 {
fmt.Printf(“%d less than 10\n”, v)
} else {
fmt.Printf(“%d not less than 10\n”, v)
}

與多元函式一起使用更有益處:

複製程式碼 程式碼如下:
if n, err = fd.Write(buf); err != nil { … }

省略條件意為true,在這裡沒有什麼用。但在for,switch語句中尤其有用。

for

基本形式是大家所熟知的:

複製程式碼 程式碼如下:
for i := 0; i < 10; i { … }

省略條件意為true:

複製程式碼 程式碼如下:
for ;; { fmt.Printf(“looping forever”) }

而且你還可以省略分號:

複製程式碼 程式碼如下:
for { fmt.Printf(“Mine! “) }

不要忘記多項賦值:

複製程式碼 程式碼如下:
for i,j := 0,N; i < j; i,j = i 1,j-1 {…}

(Go中沒有像C中那樣的逗號操作符)

switch細節

switch與C中的switch有些類似。

不過,有一些語法和語義的重要不同之處:

– 表示式不必一定是常量,甚至可以不必是int。
– 沒有自動的fall through
– 但作為替代,語法上,最後的語句可以為fallthrough
– 多case可以用逗號分隔

複製程式碼 程式碼如下:
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要遠比C中的強大。常見的形式:

複製程式碼 程式碼如下:
switch a {
case 0: fmt.Printf(“0”)
default: fmt.Printf(“non-zero”)
}

switch表示式可以是任意型別,如果為空,則表示true。結果類似一個if-else鏈:

複製程式碼 程式碼如下:
a, b := x[i], y[j]
switch {
case a < b: return -1
case a == b: return 0
case a > b: return 1
}

複製程式碼 程式碼如下:
switch a, b := x[i], y[j]; { … }

Break,continue等

break和continue語句的工作方式與C中的類似。

它們可以指定一個label並影響外層結構:

複製程式碼 程式碼如下:
Loop: for i := 0; i < 10; i {
switch f(i) {
case 0, 1, 2: break Loop
}
g(i)
}

是的,那是一個goto。

十、函式

函式以func關鍵字開頭。

如果有返回型別,返回型別放在引數的後面。return的含義和你期望的一致。
複製程式碼 程式碼如下:
func square(f float64) float64 { return f*f }

函式支援返回多個值。這樣,返回型別就是一個括號包圍的列表。
複製程式碼 程式碼如下:
func MySqrt(f float64) (float64, bool) {
if f >= 0 { return math.Sqrt(f), true }
return 0, false
}

空識別符號

如果你只關心MySqrt函式返回的第一個值?你仍然需要將第二個值放在一個地方。

解決方法:使用空識別符號_(下劃線)。它是預宣告的,可以被賦予任何無用的值。

複製程式碼 程式碼如下:
// Don’t care about boolean from MySqrt.
val, _ = MySqrt(foo())

在空識別符號其他的適用場合中,我們仍然會展示它。

帶結果變數(result variable)的函式

如果你給結果引數命名了,你可以將它當作實際變數使用。
複製程式碼 程式碼如下:
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
}

結果變數被初始化為”0″(0,0.0,false等。根據其型別;稍後有更多有關內容)
複製程式碼 程式碼如下:
func MySqrt(f float64) (v float64, ok bool) {
if f >= 0 { v,ok = math.Sqrt(f), true }
return v,ok
}

空返回

最後,一個沒有返回表示式的return將返回結果變數的當前值。下面是另外兩個MySqrt的版本:
複製程式碼 程式碼如下:
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中的記憶體都是被初始化了的。所有變數在執行之前的宣告時被初始化。如果沒有顯式的初始化表示式,我們將使用對應型別的”0值”。下面的迴圈:
複製程式碼 程式碼如下:
for i := 0; i < 5; i {
var v int
fmt.Printf(“%d “, v)
v = 5
}

將列印0 0 0 0 0。

0值取決於型別:數值是0;布林是false;空字串是””;指標,map、切片、channel是nil;結構體是0等。

Defer

defer語句負責在其所在的函式返回時執行一個函式(或方法)。其引數在到達defer語句那個時刻被求值;其函式在返回時被執行。
複製程式碼 程式碼如下:
func data(fileName string) string {
f := os.Open(fileName)
defer f.Close()
contents := io.ReadAll(f)
return contents
}

在關閉檔案描述符、解互斥鎖等場合十分有用。

每Defer執行一個函式

Go按按後入先出(LIFO)次序執行一組defer函式。
複製程式碼 程式碼如下:
func f() {
for i := 0; i < 5; i {
defer fmt.Printf(“%d “, i)
}
}

上面程式碼將輸出4 3 2 1 0。你可以在最後關閉所有檔案描述符以及解鎖所有互斥鎖。

用defer跟蹤程式碼:

複製程式碼 程式碼如下:
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() }

不過我們可以實現的更靈巧一些。

引數當即求值,defer稍後執行
複製程式碼 程式碼如下:
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() }

函式字面值

和在C中一樣,函式不能在函式內部宣告。但函式字面值卻可以被賦值給變數。
複製程式碼 程式碼如下:
func f() {
for i := 0; i < 10; i {
g := func(i int) { fmt.Printf(“%d”,i) }
g(i)
}
}

函式字面值是閉包(closure)

函式字面值實際上是閉包。
複製程式碼 程式碼如下:
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語io包的常用介面Go語言的os包中常用函式初步歸納舉例講解Go語言中函式的閉包使用深入理解Go語言中的閉包go語言裡包的用法例項Go語言宣告一個多行字串的變數GO語言獲取系統環境變數的方法Go語言中的變數宣告和賦值詳解Golang程式設計中的常量與變數go語言學習之包和變數詳解