【译】Go语言声明语法
引言
Go語言的初學(xué)者可能會(huì)好奇為什么Go的類型聲明語法和傳統(tǒng)的C系語言不同。本篇文章我們將比較這兩種不同的類型聲明方式,解釋為什么Go的聲明會(huì)是這樣子。
C語法
首先,我們談?wù)凜的語法。C采用了一種不同尋常且聰明的類型聲明語法。它沒有使用特殊語法來描述變量類型,而是使用一個(gè)包含要聲明變量的表達(dá)式,并陳述這個(gè)表達(dá)式的類型。因此
int x; 復(fù)制代碼聲明x是一個(gè)int,因?yàn)楸磉_(dá)式'X'是int類型。通常,為了弄清楚如何聲明一個(gè)新變量,首先得寫一個(gè)包含這個(gè)變量的表達(dá)式,這個(gè)表達(dá)式本身是一個(gè)基礎(chǔ)類型。然后,把這個(gè)基礎(chǔ)類型放在左邊,表達(dá)式放在右邊。
因此,下面的幾個(gè)類型聲明:
聲明p是一個(gè)int指針,因?yàn)楸磉_(dá)式”*p“是int類型。而a是一個(gè)int數(shù)組,因?yàn)閍[3](忽略其中的數(shù)字,這個(gè)數(shù)字是用來指定數(shù)組的大小的)表達(dá)式是int類型。
再來看看函數(shù)。最開始,C的函數(shù)聲明將參數(shù)的類型寫在小括號(hào)外,像這樣:
同樣的,我們看到main是一個(gè)函數(shù),因?yàn)楸磉_(dá)式main(argc, argv)返回一個(gè)int。現(xiàn)在的寫法是:
int main(int argc, char *argv[]) { /* ... */ } 復(fù)制代碼但基本結(jié)構(gòu)都是一樣的。
這種語法在類型簡(jiǎn)單的時(shí)候沒有問題,但在復(fù)雜的情況下很容易讓人迷惑。一個(gè)著名的例子就是聲明一個(gè)函數(shù)指針。根據(jù)以上規(guī)則,應(yīng)該這么寫:
其中,fp是一個(gè)函數(shù)指針,因?yàn)楸磉_(dá)式(*fp)(a,b)表示調(diào)用一個(gè)返回int的函數(shù)。那如果fp的某個(gè)參數(shù)也是一個(gè)函數(shù)呢?
int (*fp)(int (*ff)(int x, int y), int b) 復(fù)制代碼這就開始變得難以讀懂了。
當(dāng)然,我們?cè)诼暶饕粋€(gè)函數(shù)時(shí)可以去掉參數(shù)的名字,所以main可以這樣聲明
回憶一下,參數(shù)argv的聲明是這樣的,
char *argv[] 復(fù)制代碼所以你從這個(gè)聲明語句的中間去掉了變量的名字來構(gòu)建它的類型。這樣如果你需要聲明一個(gè)char *[]的變量,你得把變量名放在char *[]中間,顯然這樣不夠直觀。
如果不聲明參數(shù)的名字,來看看fp的聲明會(huì)是什么樣?
這樣不僅不知道變量名該放在哪
int (*)(int, int) 復(fù)制代碼而且很難看出來這是在聲明一個(gè)函數(shù)指針。進(jìn)一步,如果該函數(shù)的返回類型也是一個(gè)函數(shù)指針呢?
int (*(*fp)(int (*)(int, int), int))(int, int) 復(fù)制代碼這里甚至很難看出這其中變量名為fp。
這樣精心構(gòu)建的例子還有很多,這就說明C的變量聲明語法能有多復(fù)雜。
還有一點(diǎn)學(xué)要提下,就是類型轉(zhuǎn)換和類型聲明語法是一樣的,這使得C語言很難解析表達(dá)式中的類型轉(zhuǎn)換,這也就是為什么C系語言總是在類型轉(zhuǎn)換的時(shí)候打括號(hào),例如
Go語法
非C系語言聲明一個(gè)類型聲明的語法通常是很清晰的。首先給出變量名字,然后接一個(gè)冒號(hào),后面是類型。那么,上面的例子就變成了這樣(在一個(gè)解釋型的函數(shù)語言中)
x: int p: pointer to int a: array[3] of int 復(fù)制代碼這些聲明非常清晰,你只需要從左向右讀。Go借鑒了這些,但更簡(jiǎn)潔,去掉了冒號(hào)和一些關(guān)鍵字:
x int p *int a [3]int 復(fù)制代碼[3]int的形式和如何在表達(dá)式中使用變量a沒有什么直接的聯(lián)系。通過這種分開的語法,表達(dá)的更清楚。
再看看函數(shù)。將上面的main函數(shù)使用Go表達(dá)出來(Go中main是沒有參數(shù)的):
表面上看起來似乎和C沒有多大差別,僅僅把char改成了strings。但Go版本更加適合從左向右閱讀:
main函數(shù)接受一個(gè)int和一個(gè)strings的切片,返回一個(gè)int。
去掉參數(shù)名也一樣簡(jiǎn)潔,因?yàn)樽兞棵肋h(yuǎn)在聲明的最前面,所以這不會(huì)造成迷惑。
這種從左到右的聲明語法有一個(gè)非常實(shí)用的地方:當(dāng)類型非常復(fù)雜的時(shí)候,它也能很清晰。下面是一個(gè)函數(shù)變量的聲明(類比C中的函數(shù)指針):
f func (func(int, int) int, int) int 復(fù)制代碼或者返回值也是一個(gè)函數(shù)
f func(func(int, int) int, int) func(int, int) int 復(fù)制代碼從左到右讀起來依然非常清晰,變量的名字也非常明顯,因?yàn)樗偸堑谝淮纬霈F(xiàn)。
類型轉(zhuǎn)換和表達(dá)式之間的語法區(qū)別使得寫和調(diào)用閉包都非常容易:
指針
指針是規(guī)則中的一個(gè)特例。普通的,比如數(shù)組和切片,Go的類型聲明把中括號(hào)放在類型的左邊,但在表達(dá)式中會(huì)放在類型的右邊:
var a []int x = a[1] 復(fù)制代碼為了使用者更加熟悉,Go的指針也使用了C中*概念,但這就沒有了上面的顛倒規(guī)則,因此使用指針是這樣的:
var p *int x = *p 復(fù)制代碼而不是這樣的
var p *int x = p* 復(fù)制代碼因?yàn)楹缶Y*與乘法沖突。我們也曾使用過^這個(gè)符合,例如:
var p ^int x = p^ 復(fù)制代碼或許我們應(yīng)該這么寫(使用其他的符號(hào)表示異或),因?yàn)樵陬愋吐暶骱捅磉_(dá)式中同時(shí)使用前綴*在一些情況下會(huì)很復(fù)雜。例如
[]int("hi") 復(fù)制代碼為了類型轉(zhuǎn)換,如果類型以*開頭就必須加括號(hào):
(*int)(nil) 復(fù)制代碼如果不使用*作為指針語法,那就不用打括號(hào)了。
所以Go的指針嘗試接近C的形式,這就意味著不能完全去掉區(qū)分類型聲明和表達(dá)式的括號(hào)。 總的來說,我們相信Go的類型聲明語法比C更容易理解,特別是在復(fù)雜的情況下。
總結(jié)
Go的類型聲明語法是從左到右的,而C的類型聲明語法則是螺旋形的!具體參考David Anderson的這篇文章 The "Clockwise/Spiral Rule"
個(gè)人總結(jié)
Go語言類型聲明語法中,永遠(yuǎn)將變量名放在最前面,這比C系語言更加清晰,更適合從左向右閱讀。
但是類型轉(zhuǎn)換似乎沒有多大區(qū)別,都需要括號(hào),只是括號(hào)的對(duì)象不同而已:
原文
Go's Declaration Syntax
總結(jié)
以上是生活随笔為你收集整理的【译】Go语言声明语法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 长春初二数学辅导班|长春初二数学补习班
- 下一篇: 南京人工智能高等研究院孔慧:多向技术驱动