go的声明语法

简介

刚接触go的人一定会有个疑惑,为什么go的声明语法与传统的C簇类语言不一样。本文我们将通过比较两种语言语法,来解释为啥go语言要这样做。

C簇语法

首先,我们来谈一谈C的语法,其采用了一种不常用且巧妙的方式,可称为”解方程式”的语法。一个表达式涉及了描述实例和类型定义。例如:

int x;

定义x是一个int:因为x具有int类型。通常情况,C声明一个新变量,都需要思考它是什么类型,将类型写在左侧,变量名写在右侧。

int *p;
int a[3];

p是一个指向int的指针,这是因为*p的类型是int,同样道理, a[3]是一个int数组,因为a[3]的类型是int (数值3表示数组的大小)
那函数是怎么样呢?最初C函数的参数类型声明是写在()外面,例如:

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

可以看出,定义了 一个main函数,有两个参数 argc,argv,参数的定义写在了main(argc, argv)的下面,函数返回值是int。在来看看现代C的写法:

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

两种写法基本结构差不多,这种解方程式的语法思想,适用于简单的类型定义,如果一复杂就容易引起混淆。最有名的的一个例子就是声明一个函数指针,遵循上面语法思想,可以写成:

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

上面表达式声明了fp是一个指向函数的指针,因为 (*fp)(a, b)的返回类型是int。让我们再来思考一个问题,如果函数的参数也是一个函数,那又该如何。

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

可以发现这个函数声明变得难读了。
当然我们也可以尝试去掉参数名,来定义函数,main函数的声明就简化如下:

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

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

char *argv[]

所以去掉参数名后,变的非常不直观。这时我们再来看看,如果去掉参数名,fp的声明将会变成什么样:

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

看其中一个参数:

int (*)(int, int)

不仅在哪里放置参数名不明显,而且也并不能清晰的描述这是一个函数指针。我们再来看一个:

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

有没有晕的感觉,像这样的例子可以举出很多,所以C的声明语法在可读性存在一定缺陷。
另外还有一点需要指出,由于类型和声明的语法是相同的,所以C在做类型转换时,必须要加上括号,与声明区分开,例如:

(int)M_PI

go语法

C系列之外的语言通常将在声明中区分类型的语法。虽然各语言有区别,但名称通常是第一个,后面跟着冒号。 因此,我们上面的例子变成了(说明性的语言)

x: int
p: pointer to int
a: array[3] of int

这种声明方式清晰易懂,针对复杂的情况,你只要遵循从左到右的读取即可。go的语法灵感源于此,不过为了简洁,去掉了冒号,以及一些关键词:

x int
p *int
a [3]int

[3]int的这种写法与如何使用a之间并没有直接的对应关系,后面你会发现,采用这种隔离的语法结构,表达式会更清晰。
现在让我们用go的语法,重写一下main函数(真正的go的main函数是没有参数):

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

与c有些不同,比如 char变成了string,从左到右阅读代码,更符合习惯。
函数main定义两个参数,一个是int,一个是string数组,并返回int。
移除参数名来看看:

func main(int, []string) int

这种从左到右的分格,在碰到复杂的情况下,也能一目了然,用函数参数为例:

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

或者返回值为一个函数:

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 []int
x = a[1]

go的指针延续了C语言的符号”*”, 这导致了我们并能像数组一样做反转。正确的写法:

var p *int
x = *p

而不是

var p *int
x = p*

因为后缀*会与乘法相混淆。 试想一下使用 ^代替,例如:

var p ^int
x = p^

也许我们应该这么样做(重新给异或定义一个字符),因为前缀*对于类型和表达式在很多方面都是复杂的。 例如,虽然可以写:

[]int("hi"

如果以*开头,则必须将该类型括起来:

(*int)(nil)

如果我们放弃*做为指针语法,这样这些括号就没有必要了。
Go的指针语法与C有千丝万缕的关系,这意味着我们在类型和表达式语法中无法消除括号,但总的来说,我们认为Go的类型语法比C更易懂,特别是在越复杂的情景下体现的越明显。

欢迎关注公众号
这里写图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值