Go語言專題(二):Go語言基礎結構

Go語言的基礎組成

在我們開始學習 Go 編程語言的基礎構建模塊前,讓我們先來了解 Go 語言最簡單程序的結構。

Go 語言的基礎組成有以下幾個部分:

.引入包

.函數

.變量

.語句&表達式

. 註釋

接下來讓我們來看下簡單的代碼,該代碼輸出了"Hello World!":

packagemain

import"fmt"

funcmain() {
/* 這是我的第一個簡單的程序 */
fmt.Println("Hello, World!")
}

讓我們來看下以上程序的各個部分:

  1. 第一行代碼 package main 定義了包名。你必須在源文件中非註釋的第一行指明這個文件屬於哪個包,如:package main。package main表示一個可獨立執行的程序,每個 Go 應用程序都包含一個名為 main 的包。
  2. 下一行 import "fmt" 告訴 Go 編譯器這個程序需要使用 fmt 包(的函數,或其他元素),fmt 包實現了格式化 IO(輸入/輸出)的函數。
  3. 下一行 func main() 是程序開始執行的函數。main 函數是每一個可執行程序所必須包含的,一般來說都是在啟動後第一個執行的函數(如果有 init() 函數則會先執行該函數)。
  4. 下一行 /.../ 是註釋,在程序執行時將被忽略。單行註釋是最常見的註釋形式,你可以在任何地方使用以 // 開頭的單行註釋。多行註釋也叫塊註釋,均已以 /* 開頭,並以 */ 結尾,且不可以嵌套使用,多行註釋一般用於包的文檔描述或註釋成塊的代碼片段。
  5. 下一行 fmt.Println(...) 可以將字符串輸出到控制檯,並在最後自動增加換行字符 \n。
  6. 使用 fmt.Print("hello, world\n") 可以得到相同的結果。
  7. Print 和 Println 這兩個函數也支持使用變量,如:fmt.Println(arr)。如果沒有特別指定,它們會以默認的打印格式將變量 arr 輸出到控制檯。
  8. 當標識符(包括常量、變量、類型、函數名、結構字段等等)以一個大寫字母開頭,如:Group1,那麼使用這種形式的標識符的對象就可以被外部包的代碼所使用(客戶端程序需要先導入這個包),這被稱為導出(像面嚮對象語言中的 public);標識符如果以小寫字母開頭,則對包外是不可見的,但是他們在整個包的內部是可見並且可用的(像面嚮對象語言中的 protected )。

執行 Go 程序

讓我們來看下如何編寫 Go 代碼並執行它。步驟如下:

  1. 打開編輯器如Sublime2,將以上代碼添加到編輯器中。
  2. 將以上代碼保存為 hello.go
  3. 打開命令行,並進入程序文件保存的目錄中。
  4. 輸入命令 go run hello.go 並按回車執行代碼。
  5. 如果操作正確你將在屏幕上看到 "Hello World!" 字樣的輸出。
$ go run hello.go
Hello, World!

注意

需要注意的是 { 不能單獨放在一行,所以以下代碼在運行時會產生錯誤:

packagemain

import"fmt"

funcmain()
{ // 錯誤,{ 不能在單獨的行上

fmt.Println("Hello, World!")
}

Go 語言基礎語法

Go 標記

Go 程序可以由多個標記組成,可以是關鍵字,標識符,常量,字符串,符號。如以下 GO 語句由 6 個標記組成:

fmt.Println("Hello, World!")

6 個標記是(每行一個):

1.fmt
2..
3.Println
4.(
5."Hello, World!"
6.)

行分隔符

在 Go 程序中,一行代表一個語句結束。每個語句不需要像 C 家族中的其它語言一樣以分號 ; 結尾,因為這些工作都將由 Go 編譯器自動完成。

如果你打算將多個語句寫在同一行,它們則必須使用 ; 人為區分,但在實際開發中我們並不鼓勵這種做法。

以下為兩個語句:

fmt.Println("Hello, World!")
fmt.Println("菜鳥教程:runoob.com")

註釋

註釋不會被編譯,每一個包應該有相關注釋。

單行註釋是最常見的註釋形式,你可以在任何地方使用以 // 開頭的單行註釋。多行註釋也叫塊註釋,均已以 /* 開頭,並以 */ 結尾。如:

// 單行註釋
/*
Author by 菜鳥教程
我是多行註釋
*/

標識符

標識符用來命名變量、類型等程序實體。一個標識符實際上就是一個或是多個字母(A~Z和a~z)數字(0~9)、下劃線_組成的序列,但是第一個字符必須是字母或下劃線而不能是數字。

以下是有效的標識符:

mahesh kumar abc move_name a_123 

myname50 _temp j a23b9 retVal

以下是無效的標識符:

.1ab(以數字開頭)

.case(Go 語言的關鍵字)

.a+b(運算符是不允許的)

關鍵字

下面列舉了 Go 代碼中會使用到的 25 個關鍵字或保留字:

除了以上介紹的這些關鍵字,Go 語言還有 36 個預定義標識符:

程序一般由關鍵字、常量、變量、運算符、類型和函數組成。

程序中可能會使用到這些分隔符:括號 (),中括號 [] 和大括號 {}。

程序中可能會使用到這些標點符號:.、,、;、: 和 …。

Go 語言的空格

Go 語言中變量的聲明必須使用空格隔開,如:

varageint; 

語句中適當使用空格能讓程序更易閱讀。

無空格:

fruit=apples+oranges;

在變量與運算符間加入空格,程序看起來更加美觀,如:

fruit=apples+oranges; 

Go 語言數據類型

在 Go 編程語言中,數據類型用於聲明函數和變量。

數據類型的出現是為了把數據分成所需內存大小不同的數據,編程的時候需要用大數據的時候才需要申請大內存,就可以充分利用內存。

Go 語言按類別有以下幾種數據類型:

數字類型

Go 也有基於架構的類型,例如:int、uint 和 uintptr。

浮點型:

其他數字類型

以下列出了其他更多的數字類型:

Go語言變量

變量來源於數學,是計算機語言中能儲存計算結果或能表示值抽象概念。變量可以通過變量名訪問。

Go 語言變量名由字母、數字、下劃線組成,其中首個字母不能為數字。

聲明變量的一般形式是使用 var 關鍵字:

varidentifiertype

第一種,指定變量類型,聲明後若不賦值,使用默認值。

varv_namev_type
v_name=value

第二種,根據值自行判定變量類型。

varv_name=value

第三種,省略var, 注意 :=左側的變量不應該是已經聲明過的,否則會導致編譯錯誤。

v_name:=value

// 例如
varaint=10
varb=10
c:=10

實例如下:

packagemain

vara="sky"
varbstring="www.baidu.com"
varcbool

funcmain(){
println(a,b,c)
}

以上實例執行結果為:

sky www.baidu.com false
//類型相同多個變量, 非全局變量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3

var vname1, vname2, vname3 = v1, v2, v3 //和python很像,不需要顯示聲明類型,自動推斷

vname1, vname2, vname3 := v1, v2, v3 //出現在:=左側的變量不應該是已經被聲明過的,否則會導致編譯錯誤


// 這種因式分解關鍵字的寫法一般用於聲明全局變量
var (
vname1 v_type1
vname2 v_type2
)

實例如下:

packagemain

varx, yint
var( // 這種因式分解關鍵字的寫法一般用於聲明全局變量
aint
bbool
)

varc, dint=1, 2
vare, f=123, "hello"

//這種不帶聲明格式的只能在函數體中出現
//g, h := 123, "hello"

funcmain(){
g, h:=123, "hello"
println(x, y, a, b, c, d, e, f, g, h)
}

以上實例執行結果為:

0 0 0 false 1 2 123 hello 123 hello

值類型和引用類型

所有像 int、float、bool 和 string 這些基本類型都屬於值類型,使用這些類型的變量直接指向存在內存中的值:

Go語言專題(二):Go語言基礎結構

當使用等號 = 將一個變量的值賦值給另一個變量時,如:j = i,實際上是在內存中將 i 的值進行了拷貝:

Go語言專題(二):Go語言基礎結構

你可以通過 &i 來獲取變量 i 的內存地址,例如:0xf840000040(每次的地址都可能不一樣)。值類型的變量的值存儲在棧中。

內存地址會根據機器的不同而有所不同,甚至相同的程序在不同的機器上執行後也會有不同的內存地址。因為每臺機器可能有不同的存儲器佈局,並且位置分配也可能不同。

更復雜的數據通常會需要使用多個字,這些數據一般使用引用類型保存。

一個引用類型的變量 r1 存儲的是 r1 的值所在的內存地址(數字),或內存地址中第一個字所在的位置。

Go語言專題(二):Go語言基礎結構

這個內存地址為稱之為指針,這個指針實際上也被存在另外的某一個字中。

同一個引用類型的指針指向的多個字可以是在連續的內存地址中(內存佈局是連續的),這也是計算效率最高的一種存儲形式;也可以將這些字分散存放在內存中,每個字都指示了下一個字所在的內存地址。

當使用賦值語句 r2 = r1 時,只有引用(地址)被複制。

如果 r1 的值被改變了,那麼這個值的所有引用都會指向被修改後的內容,在這個例子中,r2 也會受到影響。

簡短形式,使用 := 賦值操作符

我們知道可以在變量的初始化時省略變量的類型而由系統自動推斷,聲明語句寫上 var 關鍵字其實是顯得有些多餘了,因此我們可以將它們簡寫為 a := 50 或 b := false。

a 和 b 的類型(int 和 bool)將由編譯器自動推斷。

這是使用變量的首選形式,但是它只能被用在函數體內,而不可以用於全局變量的聲明與賦值。使用操作符 := 可以高效地創建一個新的變量,稱之為初始化聲明。

注意事項

如果在相同的代碼塊中,我們不可以再次對於相同名稱的變量使用初始化聲明,例如:a := 20 就是不被允許的,編譯器會提示錯誤 no new variables on left side of :=,但是 a = 20 是可以的,因為這是給相同的變量賦予一個新的值。

如果你在定義變量 a 之前使用它,則會得到編譯錯誤 undefined: a。

如果你聲明瞭一個局部變量卻沒有在相同的代碼塊中使用它,同樣會得到編譯錯誤,例如下面這個例子當中的變量 a:

packagemain

import"fmt"

funcmain() {
varastring="abc"
fmt.Println("hello, world")
}

嘗試編譯這段代碼將得到錯誤 a declared and not used。

此外,單純地給 a 賦值也是不夠的,這個值必須被使用,所以使用

fmt.Println("hello, world", a)

會移除錯誤。

但是全局變量是允許聲明但不使用。 同一類型的多個變量可以聲明在同一行,如:

vara, b, cint

多變量可以在同一行進行賦值,如:

vara, bint
varcstring
a, b, c=5, 7, "abc"

上面這行假設了變量 a,b 和 c 都已經被聲明,否則的話應該這樣使用:

a, b, c:=5, 7, "abc"

右邊的這些值以相同的順序賦值給左邊的變量,所以 a 的值是 5, b 的值是 7,c 的值是 "abc"。

這被稱為 並行 或 同時 賦值。

如果你想要交換兩個變量的值,則可以簡單地使用 a, b = b, a,兩個變量的類型必須是相同。

空白標識符 _ 也被用於拋棄值,如值 5 在:_, b = 5, 7 中被拋棄。

_ 實際上是一個只寫變量,你不能得到它的值。這樣做是因為 Go 語言中你必須使用所有被聲明的變量,但有時你並不需要使用從一個函數得到的所有返回值。

並行賦值也被用於當一個函數返回多個返回值時,比如這裡的 val 和錯誤 err 是通過調用 Func1 函數同時得到:val, err = Func1(var1)。

Go 語言常量

常量是一個簡單值的標識符,在程序運行時,不會被修改的量。

常量中的數據類型只可以是布爾型、數字型(整數型、浮點型和複數)和字符串型。

常量的定義格式:

constidentifier[type] =value

你可以省略類型說明符 [type],因為編譯器可以根據變量的值來推斷其類型。

顯式類型定義: const b string = "abc"

隱式類型定義: const b = "abc"

constc_name1, c_name2=value1, value2

以下實例演示了常量的應用:

packagemain

import"fmt"

funcmain() {
constLENGTHint=10

constWIDTHint=5
varareaint
consta, b, c=1, false, "str"//多重賦值

area=LENGTH*WIDTH
fmt.Printf("面積為 : %d", area)
println()
println(a, b, c)
}

以上實例運行結果為:

面積為 : 50
1 false str

常量還可以用作枚舉:

const(
Unknown=0
Female=1
Male=2
)

數字 0、1 和 2 分別代表未知性別、女性和男性。

常量可以用len(), cap(), unsafe.Sizeof()函數計算表達式的值。常量表達式中,函數必須是內置函數,否則編譯不過:

packagemain

import"unsafe"
const(
a="abc"
b=len(a)
c=unsafe.Sizeof(a)
)

funcmain(){
println(a, b, c)
}

以上實例運行結果為:

abc 3 16

iota

iota,特殊常量,可以認為是一個可以被編譯器修改的常量。

iota 在 const關鍵字出現時將被重置為 0(const 內部的第一行之前),const 中每新增一行常量聲明將使 iota 計數一次(iota 可理解為 const 語句塊中的行索引)。

iota 可以被用作枚舉值:

const(
a=iota
b=iota
c=iota
)

第一個 iota 等於 0,每當 iota 在新的一行被使用時,它的值都會自動加 1;所以 a=0, b=1, c=2 可以簡寫為如下形式:

const(
a=iota
b
c
)

iota 用法

packagemain

import"fmt"

funcmain() {
const(
a=iota //0
b //1
c //2
d="ha" //獨立值,iota += 1
e //"ha" iota += 1
f=100 //iota +=1
g //100 iota +=1
h=iota //7,恢復計數
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
}

以上實例運行結果為:

0 1 2 ha ha 100 100 7 8

再看個有趣的的 iota 實例:

packagemain

import"fmt"
const(
i=1<<iota> j=3<<iota> k
l
)

funcmain() {
fmt.Println("i=",i)
fmt.Println("j=",j)
fmt.Println("k=",k)
fmt.Println("l=",l)
}
/<iota>/<iota>

以上實例運行結果為:

i= 1
j= 6

k= 12
l= 24

iota 表示從 0 開始自動加 1,所以 i=1<<0, j=3<<1(<< 表示左移的意思),即:i=1, j=6,這沒問題,關鍵在 k 和 l,從輸出結果看 k=3<<2,l=3<<3。

簡單表述:

  • i=1
  • :左移 0 位,不變仍為 1;
  • j=3:左移 1 位,變為二進制 110, 即 6;
  • k=3:左移 2 位,變為二進制 1100, 即 12;
  • l=3:左移 3 位,變為二進制 11000,即 24。

Go 語言運算符

運算符用於在程序運行時執行數學或邏輯運算。

Go 語言內置的運算符有:

  • 算術運算符
  • 關係運算符
  • 邏輯運算符
  • 位運算符
  • 賦值運算符
  • 其他運算符

接下來讓我們來詳細看看各個運算符的介紹。

算術運算符

下表列出了所有Go語言的算術運算符。假定 A 值為 10,B 值為 20。

以下實例演示了各個算術運算符的用法:

packagemain

import"fmt"

funcmain() {

varaint=21
varbint=10
varcint

c=a+b
fmt.Printf("第一行 - c 的值為 %d\n", c)
c=a-b
fmt.Printf("第二行 - c 的值為 %d\n", c)
c=a*b
fmt.Printf("第三行 - c 的值為 %d\n", c)
c=a/b
fmt.Printf("第四行 - c 的值為 %d\n", c)
c=a%b
fmt.Printf("第五行 - c 的值為 %d\n", c)
a++
fmt.Printf("第六行 - a 的值為 %d\n", a)
a=21 // 為了方便測試,a 這裡重新賦值為 21

a--
fmt.Printf("第七行 - a 的值為 %d\n", a)
}

以上實例運行結果:

第一行 - c 的值為 31
第二行 - c 的值為 11
第三行 - c 的值為 210
第四行 - c 的值為 2
第五行 - c 的值為 1
第六行 - a 的值為 22
第七行 - a 的值為 20

關係運算符

下表列出了所有Go語言的關係運算符。假定 A 值為 10,B 值為 20。

以下實例演示了關係運算符的用法:

packagemain

import"fmt"

funcmain() {
varaint=21
varbint=10

if( a==b) {
fmt.Printf("第一行 - a 等於 b\n")
} else{
fmt.Printf("第一行 - a 不等於 b\n")
}
if( a fmt.Printf("第二行 - a 小於 b\n")

} else{
fmt.Printf("第二行 - a 不小於 b\n")
}

if( a>b) {
fmt.Printf("第三行 - a 大於 b\n")
} else{
fmt.Printf("第三行 - a 不大於 b\n")
}
/* Lets change value of a and b */
a=5
b=20
if( a<=b) {
fmt.Printf("第四行 - a 小於等於 b\n")
}
if( b>=a) {
fmt.Printf("第五行 - b 大於等於 a\n")
}
}

以上實例運行結果:

第一行 - a 不等於 b
第二行 - a 不小於 b
第三行 - a 大於 b
第四行 - a 小於等於 b
第五行 - b 大於等於 a

邏輯運算符

下表列出了所有Go語言的邏輯運算符。假定 A 值為 True,B 值為 False。

以下實例演示了邏輯運算符的用法:

packagemain

import"fmt"

funcmain() {
varabool=true
varbbool=false
if( a&&b) {
fmt.Printf("第一行 - 條件為 true\n")
}
if( a||b) {
fmt.Printf("第二行 - 條件為 true\n")
}
/* 修改 a 和 b 的值 */
a=false
b=true
if( a&&b) {
fmt.Printf("第三行 - 條件為 true\n")
} else{
fmt.Printf("第三行 - 條件為 false\n")
}
if( !(a&&b) ) {
fmt.Printf("第四行 - 條件為 true\n")
}
}

以上實例運行結果:

第二行 - 條件為 true
第三行 - 條件為 false
第四行 - 條件為 true

位運算符

位運算符對整數在內存中的二進制位進行操作。

下表列出了位運算符 &, |, 和 ^ 的計算:

假定 A = 60; B = 13; 其二進制數轉換為:

A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001

Go 語言支持的位運算符如下表所示。假定 A 為60,B 為13:

以下實例演示了位運算符的用法:

packagemain

import"fmt"

funcmain() {

varauint=60 /* 60 = 0011 1100 */
varbuint=13 /* 13 = 0000 1101 */
varcuint=0

c=a&b /* 12 = 0000 1100 */
fmt.Printf("第一行 - c 的值為 %d\n", c)

c=a|b /* 61 = 0011 1101 */
fmt.Printf("第二行 - c 的值為 %d\n", c)

c=a^b /* 49 = 0011 0001 */
fmt.Printf("第三行 - c 的值為 %d\n", c)

c=a<<2 /* 240 = 1111 0000 */
fmt.Printf("第四行 - c 的值為 %d\n", c)

c=a>>2 /* 15 = 0000 1111 */
fmt.Printf("第五行 - c 的值為 %d\n", c)
}

以上實例運行結果:

第一行 - c 的值為 12
第二行 - c 的值為 61
第三行 - c 的值為 49
第四行 - c 的值為 240
第五行 - c 的值為 15

賦值運算符

下表列出了所有Go語言的賦值運算符。

以下實例演示了賦值運算符的用法:

packagemain

import"fmt"

funcmain() {
varaint=21
varcint

c= a
fmt.Printf("第 1 行 - = 運算符實例,c 值為 = %d\n", c)

c+= a
fmt.Printf("第 2 行 - += 運算符實例,c 值為 = %d\n", c)

c-= a
fmt.Printf("第 3 行 - -= 運算符實例,c 值為 = %d\n", c)

c*= a
fmt.Printf("第 4 行 - *= 運算符實例,c 值為 = %d\n", c)

c/= a
fmt.Printf("第 5 行 - /= 運算符實例,c 值為 = %d\n", c)

c =200;

c<<= 2
fmt.Printf("第 6行 - <<= 運算符實例,c 值為 = %d\n", c)

c>>= 2
fmt.Printf("第 7 行 - >>= 運算符實例,c 值為 = %d\n", c)

c&= 2
fmt.Printf("第 8 行 - &= 運算符實例,c 值為 = %d\n", c)

c^= 2
fmt.Printf("第 9 行 - ^= 運算符實例,c 值為 = %d\n", c)

c|= 2
fmt.Printf("第 10 行 - |= 運算符實例,c 值為 = %d\n", c)

}

以上實例運行結果:

第 1 行 - = 運算符實例,c 值為 = 21
第 2 行 - += 運算符實例,c 值為 = 42
第 3 行 - -= 運算符實例,c 值為 = 21
第 4 行 - *= 運算符實例,c 值為 = 441
第 5 行 - /= 運算符實例,c 值為 = 21
第 6行 - <<= 運算符實例,c 值為 = 800
第 7 行 - >>= 運算符實例,c 值為 = 200
第 8 行 - &= 運算符實例,c 值為 = 0
第 9 行 - ^= 運算符實例,c 值為 = 2
第 10 行 - |= 運算符實例,c 值為 = 2

其他運算符

下表列出了Go語言的其他運算符。

以下實例演示了其他運算符的用法:

packagemain

import"fmt"

funcmain() {
varaint=4
varbint32
varcfloat32
varptr*int

/* 運算符實例 */
fmt.Printf("第 1 行 - a 變量類型為 = %T\n", a);
fmt.Printf("第 2 行 - b 變量類型為 = %T\n", b);
fmt.Printf("第 3 行 - c 變量類型為 = %T\n", c);

/* & 和 * 運算符實例 */
ptr=&a /* 'ptr' 包含了 'a' 變量的地址 */
fmt.Printf("a 的值為 %d\n", a);
fmt.Printf("*ptr 為 %d\n", *ptr);
}

以上實例運行結果:

第 1 行 - a 變量類型為 = int
第 2 行 - b 變量類型為 = int32
第 3 行 - c 變量類型為 = float32
a 的值為 4
*ptr 為 4

運算符優先級

有些運算符擁有較高的優先級,二元運算符的運算方向均是從左至右。下表列出了所有運算符以及它們的優先級,由上至下代表優先級由高到低:

當然,你可以通過使用括號來臨時提升某個表達式的整體運算優先級。

以上實例運行結果:

packagemain

import"fmt"

funcmain() {
varaint=20
varbint=10
varcint=15
vardint=5
vareint;

e=(a+b) *c/d; // ( 30 * 15 ) / 5
fmt.Printf("(a + b) * c / d 的值為 : %d\n", e);

e=((a+b) *c) /d; // (30 * 15 ) / 5
fmt.Printf("((a + b) * c) / d 的值為 : %d\n", e);

e=(a+b) *(c/d); // (30) * (15/5)
fmt.Printf("(a + b) * (c / d) 的值為 : %d\n", e);

e=a+(b*c) /d; // 20 + (150/5)
fmt.Printf("a + (b * c) / d 的值為 : %d\n", e);
}

以上實例運行結果:

(a + b) * c / d 的值為 : 90 

((a + b) * c) / d 的值為 : 90
(a + b) * (c / d) 的值為 : 90
a + (b * c) / d 的值為 : 50


分享到:


相關文章: