Go与C声明语法比较

来源:互联网 发布:ssl 端口 编辑:程序博客网 时间:2024/06/05 19:28

Go的声明语法

2010年7月7日

介绍

从其它语言转过来的程序员想知道为什么声明语法不同于C系列中建立的传统。在这篇文章中,我们将比较两种方法,并解释为什么Go的声明看起来像它们一样。

C语法

首先,我们来谈谈C语法。C对声明语法采取了一种不寻常和聪明的方法。不用描述具有特殊语法的类型,而是写入涉及正在声明的项的表达式,并且说明表达式将具有什么类型。从而

int x;

声明x为int:表达式“x”将具有int类型。一般来说,要弄清楚如何写入一个新变量的类型,写一个表达式涉及到该基础类型的变量,然后将基本类型放在左边,右边的表达式。

因此,声明

int * p;int a [3];

声明p是一个指向int的指针,因为'* p'具有int型,并且a是一个int数组,因为a [3](忽略特定的索引值,被忽略为数组的大小)具有类型int。

功能怎么样?最初,C的函数声明写出了括号外的参数类型,如下所示:

int main(argc,argv)    int argc    char * argv [];{/ * ... * /}

再次,我们看到main是一个函数,因为表达式main(argc,argv)返回一个int。在现代符号中我们会写

int main(int argc,char * argv []){/ * ... * /}

但基本结构是一样的。

这是一个聪明的句法概念,适用于简单类型,但可能会让人困惑。着名的例子是声明一个函数指针。按照规则,你得到这个:

int(* fp)(int a,int b);

这里,fp是一个指向函数的指针,因为如果你写了表达式(* fp)(a,b),你将调用一个返回int的函数。如果fp的参数本身是一个函数呢?

int(* fp)(int(* ff)(int x,int y),int b)

这开始变得很难阅读。

当然,当我们声明一个函数时,我们可以省略参数的名称,所以main可以被声明

int main(int,char * [])

回想一下,argv是这样声明的,

char * argv []

所以你从它的声明的中间删除名称来构造它的类型。不过很明显,你通过把它的名字放在中间来声明一些类型为char * []的东西。

看看如果你没有命名参数,fp的声明会发生什么:

int(* fp)(int(*)(int,int),int)

不仅不能把名字放在哪里

int(*)(int,int)

完全不清楚它是一个函数指针声明。如果返回类型是函数指针怎么办?

int(*(* fp)(int(*)(int,int),int))(int,int)

甚至很难看到这个声明是关于fp的。

您可以构建更精细的示例,但这些示例应该说明C的声明语法可能引入的一些困难。

还有一点需要做,但是。因为类型和声明语法相同,所以可能难以用中间的类型来解析表达式。这就是为什么,例如,C casts总是括号括起来,如同

(int)M_PI

去语法

C系列以外的语言通常在声明中使用不同类型的语法。虽然这是一个单独的点,但是这个名字通常是先来的,后面是冒号。因此,我们上面的例子变成了(在虚构但说明性的语言)

x:intp:指向int的指针a:int的数组[3]

这些声明是清楚的,如果详细 - 你只是从左到右阅读它们。Go从它的提示,但为了简洁起见,它删除冒号并删除一些关键字:

x intp * inta [3] int

在[3] int的外观和如何在表达式中使用a之间没有直接的对应关系。(我们将在下一节中回到指针。)以单独的语法为代价,您将获得清晰度。

现在考虑功能。让我们转载主要的声明,因为它将在Go中阅读,尽管Go中的真正主要功能没有任何论据:

func main(argc int,argv [] string)int

从表面上看,与C不同的是char数组到字符串的变化,但从左到右依次很好:

函数main需要一个int和一个字符串,并返回一个int。

删除参数名称,它一样清楚 - 它们始终是第一,所以没有混淆。

func main(int,[] string)int

这种从左到右的风格的一个优点是它的作用如何,因为类型变得更加复杂。这是一个函数变量的声明(类似于C中的函数指针):

f func(func(int,int)int,int)int

或者如果f返回一个函数:

f func(func(int,int)int,int)func(int,int)int

它仍然从左到右清楚地显示,它的名字一直是很明显的 - 名字是第一个。

类型和表达式语法之间的区别使得在Go中容易地编写和调用闭包:

sum:= func(a,b int)int {return a + b}(3,4)

指针

指针是证明规则的例外。请注意,在数组和片段中,例如,Go的类型语法将括号放在类型的左侧,但表达式语法将它们放在表达式的右侧:

var a [] intx = a [1]

为了熟悉,Go的指针使用C的*符号,但是我们不能让自己对指针类型做出类似的反转。因此指针像这样工作

var p * intx = * p

我们不能说

var p * intx = p *

因为后缀*会与乘法混合。我们可以使用Pascal ^,例如:

var p ^ intx = p ^

也许我们应该(并为xor选择另一个运算符),因为两种类型和表达式上的前缀星号都会以多种方式复杂化。例如,虽然可以写

[] int(“hi”)

作为转换,如果以*开头,则必须对该类型进行括号:

(* int)(nil)

如果我们愿意放弃*作为指针语法,那些括号就不必要了。

所以Go的指针语法与熟悉的C表单绑定在一起,但是这些关系意味着我们无法完全使用括号去清除语法中的类型和表达式。

总的来说,尽管如此,我们相信Go的类型语法比C更容易理解,特别是当事情变得复杂时。

笔记

Go的声明从左到右读。有人指出,C的螺旋式阅读!请参阅David Anderson 的“顺时针/螺旋规则”。

作者:Rob Pike

0 0