相比很多其它流行語言,Go的語法相對(duì)簡潔。 此篇文章將介紹編程中常用的代碼元素,并展示一份簡單的Go程序代碼,以便讓剛開始學(xué)Go編程的程序員對(duì)Go代碼結(jié)構(gòu)有一個(gè)大概的印象。
創(chuàng)新互聯(lián)建站長期為超過千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為欽北企業(yè)提供專業(yè)的成都網(wǎng)站制作、網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè),欽北網(wǎng)站改版等技術(shù)服務(wù)。擁有10年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。
編程和程序代碼元素
簡單來講,編程可以看作是以各種方式控制和組合計(jì)算機(jī)運(yùn)行中的各種操作,以達(dá)到各種各樣的目的。 一個(gè)操作可能從一個(gè)硬件設(shè)備讀取、或者向一個(gè)硬件設(shè)備寫入一些數(shù)據(jù),從而完成一個(gè)特定的任務(wù)。 對(duì)于現(xiàn)代計(jì)算機(jī)來說,最基本的操作是底層計(jì)算機(jī)指令,比如CPU和GPU指令。 常見的硬件設(shè)備包括內(nèi)存、磁盤、網(wǎng)卡、顯卡,顯示器、鍵盤和鼠標(biāo)等。
直接操控底層計(jì)算機(jī)指令進(jìn)行編程是非常繁瑣和容易出錯(cuò)的。 高級(jí)編程語言通過對(duì)底層指令進(jìn)行一些封裝和對(duì)數(shù)據(jù)進(jìn)行一些抽象,從而使得編程變得直觀和易于理解。
在流行高級(jí)編程語言中,一個(gè)操作通常是通過函數(shù)(function)調(diào)用或者使用操作符(operator)運(yùn)算來完成的。 大多數(shù)高級(jí)編程語言都支持一些條件和循環(huán)控制語句。 這些條件和循環(huán)控制語句可以看作是特殊的操作。 它們的語法接近于人類語言,因此一個(gè)人寫的代碼很容易被其他人理解。
在大多數(shù)高級(jí)編程語言中,數(shù)據(jù)通常被抽象為各種類型(type)和值(value)。 一個(gè)類型可以看作是值的模板。一個(gè)值可以看作是某個(gè)類型的實(shí)例。 大多數(shù)編程語言支持自定義類型和若干預(yù)聲明類型(即內(nèi)置類型)。 一門語言的類型系統(tǒng)可以說是這門語言的靈魂。
編程中常常會(huì)使用大量的值。 一些在編碼階段可確定的值可以用它們的字面形式(literal,即字面量)來表示。 為了編程靈活和不易出錯(cuò),其它的值一般使用變量(variable)和(具名)常量(named constant)來表示。
在《Go語言101》中,具名的函數(shù)、具名的值(包括變量和具名常量)、以及定義類型和類型別名將被統(tǒng)稱為代碼要素。 代碼要素名必須為標(biāo)識(shí)符(identifier)。
高級(jí)編程語言代碼將被編譯器或者解釋器轉(zhuǎn)換為底層機(jī)器碼進(jìn)行執(zhí)行。 為了幫助編譯器和解釋器解析高級(jí)語言代碼,一些單詞將被用做關(guān)鍵字(keyword)。 這些單詞不能被當(dāng)做標(biāo)識(shí)符使用。
很多現(xiàn)代高級(jí)語言使用包(package)來組織代碼。 一個(gè)包必須引入(import)另一個(gè)包才能使用另一個(gè)包中的公有(導(dǎo)出的)代碼要素。 包名和包的引入名也都必須是標(biāo)識(shí)符。
盡管高級(jí)編程語言代碼比底層機(jī)器指令友好和易懂,我們還是需要一些注釋來幫助自己和其他程序員理解我們所寫的代碼。 在下一節(jié)的程序示例中,我們可以看到很多注釋。
一個(gè)簡單的Go示例程序
為了對(duì)各種代碼元素有一個(gè)更清楚的認(rèn)識(shí),讓我們來看一個(gè)簡短的Go示例程序。 和很多其流行語言一樣,Go使用//來起始一個(gè)行注釋,使用一個(gè)/*和*/對(duì)來包裹一個(gè)塊注釋。
下面是這個(gè)Go示例程序。請(qǐng)注意閱讀其中的注釋。程序之后有更多解釋。
package main // 指定當(dāng)前源文件所在的包名
import "math/rand" // 引入一個(gè)標(biāo)準(zhǔn)庫包
const MaxRand = 16 // 聲明一個(gè)具名整型常量
// 一個(gè)函數(shù)聲明
/*
StatRandomNumbers生成一些不大于MaxRand的非負(fù)
隨機(jī)整數(shù),并統(tǒng)計(jì)和返回小于和大于MaxRand/2的隨機(jī)數(shù)
個(gè)數(shù)。輸入?yún)?shù)numRands指定了要生成的隨機(jī)數(shù)的總數(shù)。
*/
func StatRandomNumbers(numRands int) (int, int) {
// 聲明了兩個(gè)變量(類型都為int,初始值都為0)
var a, b int
// 一個(gè)for循環(huán)代碼塊
for i := 0; i numRands; i++ {
// 一個(gè)if-else條件控制代碼塊
if rand.Intn(MaxRand) MaxRand/2 {
a = a + 1
} else {
b++ // 等價(jià)于:b = b + 1
}
}
return a, b // 此函數(shù)返回兩個(gè)結(jié)果
}
// main函數(shù),或主函數(shù),是一個(gè)程序的入口函數(shù)。
func main() {
var num = 100
// 調(diào)用上面聲明的StatRandomNumbers函數(shù),
// 并將結(jié)果賦給使用短聲明語句聲明的兩個(gè)變量。
x, y := StatRandomNumbers(num)
// 調(diào)用兩個(gè)內(nèi)置函數(shù)(print和println)。
print("Result: ", x, " + ", y, " = ", num, "? ")
println(x+y == num)
}
將上面的程序代碼存盤到一個(gè)名為basic-code-element-demo.go 的文件中并使用下列命令運(yùn)行此程序:
$ go run basic-code-element-demo.go
Result: 46 + 54 = 100? true
在上面的示例程序中,單詞package、import、const、func、var、for、if、else和return均為關(guān)鍵字。 其它大多數(shù)單詞均為標(biāo)識(shí)符。 請(qǐng)閱讀關(guān)鍵字和標(biāo)識(shí)符以獲得更多關(guān)于關(guān)鍵字和標(biāo)識(shí)符的信息。
四個(gè)int(一個(gè)在第15行,另三個(gè)在第13行) 表示內(nèi)置基本類型int。int類型是Go中的基本整數(shù)類型之一。 第5行中的16、第17行中的0、 第20行中的1以及第30行的100均為整型字面量。 第35行的"Result: "是一個(gè)字符串字面量。 請(qǐng)閱讀基本類型和它們的字面量表示以獲取更多關(guān)于基本類型和它們的字面量的信息。 Go中的非基本類型(均為組合類型)將在以后的其它文章中介紹和解釋。
第20行是一個(gè)賦值語句。 第5行聲明了一個(gè)具名常量,叫做MaxRand。 第15行和第30行使用標(biāo)準(zhǔn)變量聲明語句聲明了三個(gè)變量。 第17行的變量i以及第33行的變量x和y是使用變量短聲明語句聲明的。 變量a和b在聲明的時(shí)候被指定為int類型。 編譯器會(huì)自動(dòng)推導(dǎo)出變量i、num、x和y的類型均為int類型,因?yàn)樗鼈兊某跏贾刀际钦妥置媪勘硎镜摹?請(qǐng)閱讀常量和變量以獲取什么是類型不確定值、類型推導(dǎo)、賦值、以及如何聲明變量和具名常量。
上面的示例程序中使用了很多操作符,比如第17和19行的小于比較符,第36行的等于比較符==,還有第20和36行的加法運(yùn)算符+。 第35行中的+不是一個(gè)運(yùn)算符,它是一個(gè)字符串字面量中的一個(gè)字符。 一個(gè)使用操作符的操作中涉及到的值稱為操作值(有時(shí)也可稱為運(yùn)算數(shù))。 請(qǐng)閱讀常用操作符以獲取更多關(guān)于操作符的信息。 更多操作符將在后續(xù)其它文章中介紹。
第35和36行調(diào)用了兩個(gè)內(nèi)置函數(shù)print和println。 從第13行到第26行聲明的函數(shù)StatRandomNumbers在第33行被調(diào)用。 第19行也調(diào)用了一個(gè)函數(shù) Intn。 這個(gè)函數(shù)聲明在math/rand標(biāo)準(zhǔn)庫包中。 請(qǐng)閱讀函數(shù)聲明及函數(shù)調(diào)用以獲取更多關(guān)于函數(shù)聲明及函數(shù)調(diào)用的信息。
(注意,一般print和println這兩個(gè)內(nèi)置函數(shù)并不推薦使用。 在正式的項(xiàng)目中,我們應(yīng)該盡量使用fmt標(biāo)準(zhǔn)庫包中聲明的相應(yīng)函數(shù)。 《Go語言101》只在開始的幾篇文章中使用了這兩個(gè)函數(shù)。)
第1行指定了當(dāng)前源文件所處的包的名稱。 一個(gè)Go程序的主函數(shù)(main函數(shù))必須被聲明在一個(gè)名稱為main的包中。 第3行引入了math/rand標(biāo)準(zhǔn)庫包,并以rand做為引入名。 在這個(gè)包中聲明的Intn函數(shù)將在第19行被調(diào)用。 請(qǐng)閱讀代碼包和包引入,以獲取更多關(guān)于代碼包和包引入的信息。
表達(dá)式、語句和簡單語句一文中介紹了什么是表達(dá)式和語句。特別地,此文列出了所有的簡單語句類型。 在Go代碼中,各種流程控制代碼塊中的某些部分必須為簡單語句,某些部分必須為表達(dá)式。
StatRandomNumbers函數(shù)的聲明體中使用了兩個(gè)流程控制代碼塊。 其中一個(gè)是for循環(huán)代碼塊,它內(nèi)嵌了另外一個(gè)代碼塊。 另外一個(gè)代碼塊是一個(gè)if-else條件控制代碼塊。 請(qǐng)閱讀基本流程控制語法以獲取更多關(guān)于流程控制代碼塊的信息。 更多的特殊的流程控制代碼塊將在以后的其它文章中介紹。
空行常常用來增加代碼的可讀性。 上面的程序中也包涵了很多注釋,但它們大多是為了Go初學(xué)者快速理解的目的而加入的。 我們應(yīng)該盡量使代碼自解釋,只在確實(shí)需要解釋的地方進(jìn)行注釋。
關(guān)于代碼斷行
像很多其它流行編程語言一樣,Go也使用一對(duì)大括號(hào){ and }來形成一個(gè)顯式代碼塊。但是在Go代碼中,編碼樣式風(fēng)格有一些限制。 比如,很多左大括號(hào){不能被放到下一行。 如果,上面的StatRandomNumbers被修改成如下所示,則上面的示例程序?qū)⒕幾g不通過。
func StatRandomNumbers(numRands int) (int, int)
{ // 編譯錯(cuò)誤:語法錯(cuò)誤
var a, b int
for i := 0; i numRands; i++
{ // 編譯錯(cuò)誤:語法錯(cuò)誤
if rand.Intn(MaxRand) MaxRand/2
{ // 編譯錯(cuò)誤:語法錯(cuò)誤
a = a + 1
} else {
b++
}
}
return a, b
}
一些程序員不是很喜歡這些限制。但是這些限制有兩個(gè)好處:
它們使得Go程序編譯得非???。
它們使得不同的Go程序員編寫的代碼風(fēng)格類似,從而一個(gè)Go程序員寫的代碼很容易被另一個(gè)程序員看懂。
我們可以閱讀代碼斷行規(guī)則一文以獲取更多關(guān)于代碼換行規(guī)則的細(xì)節(jié)。在目前,我們最好避免將左大括號(hào)放在下一行。 或者說,每行的非空起始字符不能是左大括號(hào)(但是,請(qǐng)記住,這不是一個(gè)普適的規(guī)則
GO是編譯性語言,所以函數(shù)的順序是無關(guān)緊要的,為了方便閱讀,建議入口函數(shù) main 寫在最前面,其余函數(shù)按照功能需要進(jìn)行排列
GO的函數(shù) 不支持嵌套,重載和默認(rèn)參數(shù)
GO的函數(shù) 支持 無需聲明變量,可變長度,多返回值,匿名,閉包等
GO的函數(shù)用 func 來聲明,且左大括號(hào) { 不能另起一行
一個(gè)簡單的示例:
輸出為:
參數(shù):可以傳0個(gè)或多個(gè)值來供自己用
返回:通過用 return 來進(jìn)行返回
輸出為:
上面就是一個(gè)典型的多參數(shù)傳遞與多返回值
對(duì)例子的說明:
按值傳遞:是對(duì)某個(gè)變量進(jìn)行復(fù)制,不能更改原變量的值
引用傳遞:相當(dāng)于按指針傳遞,可以同時(shí)改變?cè)瓉淼闹?,并且消耗的?nèi)存會(huì)更少,只有4或8個(gè)字節(jié)的消耗
在上例中,返回值 (d int, e int, f int) { 是進(jìn)行了命名,如果不想命名可以寫成 (int,int,int){ ,返回的結(jié)果都是一樣的,但要注意:
當(dāng)返回了多個(gè)值,我們某些變量不想要,或?qū)嶋H用不到,我們可以使用 _ 來補(bǔ)位,例如上例的返回我們可以寫成 d,_,f := test(a,b,c) ,我們不想要中間的返回值,可以以這種形式來舍棄掉
在參數(shù)后面以 變量 ... type 這種形式的,我們就要以判斷出這是一個(gè)可變長度的參數(shù)
輸出為:
在上例中, strs ...string 中, strs 的實(shí)際值是b,c,d,e,這就是一個(gè)最簡單的傳遞可變長度的參數(shù)的例子,更多一些演變的形式,都非常類似
在GO中 defer 關(guān)鍵字非常重要,相當(dāng)于面相對(duì)像中的析構(gòu)函數(shù),也就是在某個(gè)函數(shù)執(zhí)行完成后,GO會(huì)自動(dòng)這個(gè);
如果在多層循環(huán)中函數(shù)里,都定義了 defer ,那么它的執(zhí)行順序是先進(jìn)后出;
當(dāng)某個(gè)函數(shù)出現(xiàn)嚴(yán)重錯(cuò)誤時(shí), defer 也會(huì)被調(diào)用
輸出為
這是一個(gè)最簡單的測(cè)試了,當(dāng)然還有更復(fù)雜的調(diào)用,比如調(diào)試程序時(shí),判斷是哪個(gè)函數(shù)出了問題,完全可以根據(jù) defer 打印出來的內(nèi)容來進(jìn)行判斷,非??焖伲@種留給你們?nèi)?shí)現(xiàn)
一個(gè)函數(shù)在函數(shù)體內(nèi)自己調(diào)用自己我們稱之為遞歸函數(shù),在做遞歸調(diào)用時(shí),經(jīng)常會(huì)將內(nèi)存給占滿,這是非常要注意的,常用的比如,快速排序就是用的遞歸調(diào)用
本篇重點(diǎn)介紹了GO函數(shù)(func)的聲明與使用,下一篇將介紹GO的結(jié)構(gòu) struct
當(dāng)需要定義一個(gè)整形變量a 心里是這樣想的:我現(xiàn)在需要一個(gè)整形的變量,我要定義它,于是我先寫一個(gè)int,再思考它的名字 a ,于是就這么寫出來了int a ?。而不是我寫了個(gè)變量a,我得給它區(qū)分個(gè)類型int。2. 在調(diào)用一個(gè)方法的時(shí)候,func(abdfsasdffdg int, bagressdgf string, csdgesredg bool) ? ?那個(gè)go函數(shù)看的很亂,程序員其實(shí)根本就不怎么看參數(shù)名字是什么,而只是看需要傳入什么類型,注意力只在于int,string,bool這三個(gè),如果如上那么寫,反而影響了視線,亂系八糟的。func(int adsfasdfsdaf, string asdfasfasf, bool gwegasgs),這么寫我只注意類型,就不受名稱影響了。3. IDE自動(dòng)提示 ? ?go本身就是為快而生,定義一個(gè)結(jié)構(gòu)變量Rectangle rectangle,當(dāng)鍵盤敲下r時(shí)候,IDE會(huì)自動(dòng)給出rectangle,直接回車就出來了,反過來就的自己一個(gè)字母一個(gè)字母敲上去,蛋疼啊4. 至于go給出的解釋,當(dāng)遇到復(fù)雜函數(shù)時(shí)…… ? ?一個(gè)項(xiàng)目中能寫幾個(gè)復(fù)雜函數(shù),為了去解決這么一點(diǎn)小問題就把優(yōu)勢(shì)給犧牲了.