刚开始学习Go语言的同学都觉得Go语言的语法很特别,尤其是使用C/C++或者Java等语言的同学。比如C等语言定义变量时,类型在名字前面,而Go语言只是把类型放在变量名后面,感觉很奇怪等等。以下为原文,对这些奇怪的语法给出了比较官方可靠的解释。不要与众不同。这是为了清楚。RobPike曾在Go官方博客(原文地址:http://blog.golang.org/gos-declaration-syntax)上解释过这个问题,简要翻译如下(有限翻译错误的部分请见谅)):IntroductionGo语言的新手我经常想知道为什么这门语言的声明语法与传统的C家族语言不同。在这篇博文中,我们将做一个对比并予以解答。C的语法首先,让我们看一下C语法。C使用了一种巧妙而不同寻常的声明语法。声明变量时,只需要用目标变量的名字写一个表达式,然后在表达式中指定表达式本身的类型即可。例如:intx;上面的代码声明了变量x,它的类型是int——也就是说,表达式x是int类型的。一般来说,为了指定一个新变量的类型,我们必须编写一个包含我们要声明的变量的表达式。这个表达式运算的结果值属于某个基本类型,我们把这个基本类型写到左边的表达式中。因此,下面的语句:int*p;inta[3];表明p是一个int类型的指针,因为*p是int类型。而a是一个int数组,因为a[3]是int类型(不用管这里出现的索引值,只是用来表示数组的长度)。接下来我们看一下函数声明。C函数声明中的参数类型写在括号外,像这样:intmain(argc,argv)intargc;char*argv[];{/*...*/}如前所述,我们可以看到main是一个函数,因为表达式main(argc,argv)返回int。在现代符号中我们这样写:intmain(intargc,char*argv[]){/*...*/}虽然看起来有点不同,但基本结构是一样的。一般来说,当类型相对简单时,C的语法是聪明的。不幸的是,一旦类型开始变得复杂,C的语法就会很快变得混乱。著名的例子是函数指针,为此我们必须编写:int(*fp)(inta,intb);在这里,fp是一个指针,因为如果你写(*fp)(a,b)这样的表达式将调用一个返回int类型值的函数。如果fp的参数之一本身就是一个函数怎么办?int(*fp)(int(*ff)(intx,inty),intb)这有点难读。当然我们在声明函数的时候不需要指定参数名,所以main函数可以声明为:intmain(int,char*[])回想一下argv之前就是下面的char*argv[]你有没有发现你从声明的“中间”去掉了变量名,然后构造了它的变量类型?虽然不明显,但是当你声明一个char*[]类型的变量时,你实际上需要在变量类型的中间插入名称。我们再来看一下,如果我们不给fp的参数命名会发生什么:int(*fp)(int(*)(int,int),int)这个东西的理解难点不仅仅是记住parametername原本放在中间的int(*)(int,int)就更让人迷惑了,因为可能连这是一个函数指针声明都不清楚。让我们看看如果返回值也是函数指针类型会发生什么fp的声明没有了。您可以自己构造更复杂的示例,但这足以解释C的声明语法引入的一些复杂性。还应该指出的是,由于类型语法与声明语法相同,可能很难解析中间有类型的表达式。这就是为什么C在做类型转换的时候总是把类型括在括号里,像这样的(int)M_PIGo非C族语言的语法通常在声明时使用不同的类型语法。通常名称排在第一位,通常后跟一个冒号。这样写的话,我们上面举的例子就是这样:x:intp:pointertointa:array[3]ofint这样的语句,虽然有点冗长,但至少很清楚——你只需要从左到右就读它。Go语言采用的解决方案就是基于此,但是为了追求简洁,Go语言去掉了冒号,去掉了一些关键字,变成如下:xintp*inta[3]int表示在[3]int和a在公式中的用法没有直接的对应关系(我们将在下一节回到指针的问题)。在这一点上,您可以提高代码的清晰度,但代价是句法差异化。现在让我们考虑函数的问题。虽然在Go语言中,main函数其实是没有参数的,我们先把前面main函数的声明复制过来:funcmain(argcint,argv*[]byte)int一看就和C没什么区别,但是从左边toright读书也不错。main函数接受一个int和一个指针并返回一个int。如果此时删除参数名,它仍然很清楚——因为参数名总是在类型之前,不会造成混淆。从左到右的funcmain(int,*[]byte)int声明方式的一个价值在于,随着类型变得越来越复杂,它仍然相对简单。下面是一个函数变量的声明(相当于C语言中的函数指针)ffunc(func(int,int)int,int)int或者当它返回一个函数时:ffunc(func(int,int)int,int)func(int,int)int上面的声明读起来还是很清楚的,从左到右,很容易理解当前声明的是哪个变量名——因为变量名总是在第一位。类型语法和表达式语法之间的差异使得在Go中调用闭包更容易:sum:=func(a,bint)int{returna+b}(3,4)除了指针的一些例外。请注意,在数组和切片中,Go的类型语法将方括号放在类型的左侧,但在表达式语法中它将方括号放在右侧:vara[]intx=a[1]同样,Go的指针如下C的*表示法,但是我们写的时候,声明的时候*是在变量名的右边,但是在表达式中我们要把*放在左边:varp*intx=*p不能这样写像这样varp*intx=p*因为后缀*可能会和乘法运算混淆,也许我们可以用Pascal的^表示法代替,像这样varp^intx=p^我们真的应该像上面那样把*改成^(of当然,如果你这样改变异或运算的符号,你就得改变),因为类型和表达式中的*前缀确实让很多事情变得有点复杂。比如,虽然我们可以这样写[]int("hi"),但是在转换的时候,如果类型以*开头,就得加上括号:(*int)(nil)如果有一天我们愿意放弃使用*作为指针语法,那么上面的括号就可以省略了。可以看出Go的指针语法和C是相似的。但是这种相似也意味着我们不能完全避免语法中有时需要用括号来避免类型和表达式产生歧义的情况。总而言之,尽管有缺点,但我们认为Go的类型语法比C的更容易理解。尤其是当类型比较复杂的时候。
