14 Go's Declaration Syntax go语言声明语法
Go's Declaration Syntax go语言声明语法
7 July 2010
Introduction
Newcomers to Go wonder why the declaration syntax is different from the tradition established in the C family. In this post we'll compare the two approaches and explain why Go's declarations look as they do.
C syntax
First, let's talk about C syntax. C took an unusual and clever approach to declaration syntax. Instead of describing the types with special syntax, one writes an expression involving the item being declared, and states what type that expression will have. Thus
int x;
declares x to be an int: the expression 'x' will have type int. In general, to figure out how to write the type of a new variable, write an expression involving that variable that evaluates to a basic type, then put the basic type on the left and the expression on the right.
Thus, the declarations
int *p;
int a[3];
state that p is a pointer to int because '*p' has type int, and that a is an array of ints because a[3] (ignoring the particular index value, which is punned to be the size of the array) has type int.
What about functions? Originally, C's function declarations wrote the types of the arguments outside the parens, like this:
int main(argc, argv)
int argc;
char *argv[];
{ /* ... */ }
Again, we see that main is a function because the expression main(argc, argv) returns an int. In modern notation we'd write
int main(int argc, char *argv[]) { /* ... */ }
but the basic structure is the same.
This is a clever syntactic idea that works well for simple types but can get confusing fast. The famous example is declaring a function pointer. Follow the rules and you get this:
int (*fp)(int a, int b);
Here, fp is a pointer to a function because if you write the expression (*fp)(a, b) you'll call a function that returns int. What if one of fp's arguments is itself a function?
int (*fp)(int (*ff)(int x, int y), int b)
That's starting to get hard to read.
Of course, we can leave out the name of the parameters when we declare a function, so main can be declared
int main(int, char *[])
Recall that argv is declared like this,
char *argv[]
so you drop the name from the middle of its declaration to construct its type. It's not obvious, though, that you declare something of type char *[] by putting its name in the middle.
And look what happens to fp's declaration if you don't name the parameters:
int (*fp)(int (*)(int, int), int)
Not only is it not obvious where to put the name inside
int (*)(int, int)
it's not exactly clear that it's a function pointer declaration at all. And what if the return type is a function pointer?
int (*(*fp)(int (*)(int, int), int))(int, int)
It's hard even to see that this declaration is about fp.
You can construct more elaborate examples but these should illustrate some of the difficulties that C's declaration syntax can introduce.
There's one more point that needs to be made, though. Because type and declaration syntax are the same, it can be difficult to parse expressions with types in the middle. This is why, for instance, C casts always parenthesize the type, as in
(int)M_PI
Go syntax
Languages outside the C family usually use a distinct type syntax in declarations. Although it's a separate point, the name usually comes first, often followed by a colon. Thus our examples above become something like (in a fictional but illustrative language)
x: int
p: pointer to int
a: array[3] of int
These declarations are clear, if verbose - you just read them left to right. Go takes its cue from here, but in the interests of brevity it drops the colon and removes some of the keywords:
x int
p *int
a [3]int
There is no direct correspondence between the look of [3]int and how to use a in an expression. (We'll come back to pointers in the next section.) You gain clarity at the cost of a separate syntax.
Now consider functions. Let's transcribe the declaration for main as it would read in Go, although the real main function in Go takes no arguments:
func main(argc int, argv []string) int
Superficially that's not much different from C, other than the change from char
arrays to strings, but it reads well from left to right:
function main takes an int and a slice of strings and returns an int.
Drop the parameter names and it's just as clear - they're always first so there's no confusion.
func main(int, []string) int
One merit of this left-to-right style is how well it works as the types become more complex. Here's a declaration of a function variable (analogous to a function pointer in C):
f func(func(int,int) int, int) int
Or if f returns a function:
f func(func(int,int) int, int) func(int, int) int
It still reads clearly, from left to right, and it's always obvious which name is being declared - the name comes first.
The distinction between type and expression syntax makes it easy to write and invoke closures in Go:
sum := func(a, b int) int { return a+b } (3, 4)
Pointers
Pointers are the exception that proves the rule. Notice that in arrays and slices, for instance, Go's type syntax puts the brackets on the left of the type but the expression syntax puts them on the right of the expression:
var a []int
x = a[1]
For familiarity, Go's pointers use the * notation from C, but we could not bring ourselves to make a similar reversal for pointer types. Thus pointers work like this
var p *int
x = *p
We couldn't say
var p *int
x = p*
because that postfix * would conflate with multiplication. We could have used the Pascal ^, for example:
var p ^int
x = p^
and perhaps we should have (and chosen another operator for xor), because the prefix asterisk on both types and expressions complicates things in a number of ways. For instance, although one can write
[]int("hi")
as a conversion, one must parenthesize the type if it starts with a *:
(*int)(nil)
Had we been willing to give up * as pointer syntax, those parentheses would be unnecessary.
So Go's pointer syntax is tied to the familiar C form, but those ties mean that we cannot break completely from using parentheses to disambiguate types and expressions in the grammar.
Overall, though, we believe Go's type syntax is easier to understand than C's, especially when things get complicated.
Notes
Go's declarations read left to right. It's been pointed out that C's read in a spiral! See The "Clockwise/Spiral Rule"by David Anderson.
By Rob Pike
Related articles
14 Go's Declaration Syntax go语言声明语法的更多相关文章
- 如何读懂复杂的C语言声明
本文已迁移至: http://www.danfengcao.info/c/c++/2014/02/25/howto-understand-complicated-declaration-of-c.ht ...
- 如何解析复杂的C语言声明
C语言中有时会出现复杂的声明,比如 char * const * (*next) (); //这是个什么东东? 在讲复杂声明的分析方法前,先来个补充点. C语言变量的声明始终贯彻两点 : ...
- C语言声明解析方法
1.C语言声明的单独语法成份 声明器是C语言声明的非常重要成份,他是所有声明的核心内容,简单的说:声明器就是标识符以及与它组合在一起的任何指针.函数括号.数组下表等,为了方便起见这里进行分类表 ...
- [C语言]声明解析器cdecl修改版
一.写在前面 K&R曾经在书中承认,"C语言声明的语法有时会带来严重的问题.".由于历史原因(BCPL语言只有唯一一个类型——二进制字),C语言声明的语法在各种合理的组合下 ...
- Why Go's Declaration Syntax is better than C++?
[Why Go's Declaration Syntax is better than C++?] Newcomers to Go wonder why the declaration syntax ...
- Go's Declaration Syntax
Introduction Newcomers to Go wonder why the declaration syntax is different from the tradition estab ...
- 02. Go 语言基本语法
Go语言基本语法 变量.数据类型和常量是编程中最常见,也是很好理解的概念.本章将从 Go 语言的变量开始,逐步介绍各种数据类型及常量. Go 语言在很多特性上和C语言非常相近.如果读者有C语言基础,那 ...
- Java语言基本语法
Java语言基本语法 一.标识符和关键字 标识符 在java语言中,用来标志类名.对象名.变量名.方法名.类型名.数组名.包名的有效字符序列,称为“标识符”: 标识符由字母.数字.下划线.美元符号组成 ...
- C语言基础语法
#include <stdio.h> int main() { int age; printf("input your age"); scanf("%d&qu ...
随机推荐
- BZOJ 3731: Gty的超级妹子树
3731: Gty的超级妹子树 Time Limit: 7 Sec Memory Limit: 32 MBSubmit: 346 Solved: 96[Submit][Status][Discus ...
- 关于idea中新建web项目 webapp文件夹没有小蓝点 ,启动服务,访问不到解决方案
第一步: 选中项目按F4键,找到你的项目. 第二步: 选中项目下的web,如果没有web点击左上角的加号,找到web最下面,添加进去 第三步: 点开type下的节点,出来弹框, 第四步: 点击弹框的选 ...
- web服务器nginx和apache的对比分析
今天准备较详细的对比一下apache httpd与nginx两个web服务器的异同点.优缺点.由于我并不是做web开发的,所以有什么理解错误还请指出,想要了解它们是因为工作中有时候会用到它, ...
- 使用VS2012调试Dump文件
前一节我讲了怎么设置C++崩溃时生成Dump文件 , 点击 传送门 , 这一节我讲讲怎么使用 VS2012 调试生成的 Dump 文件 , 甚至可以精确到出错的那一行代码上面 ; 1. 生成 Dump ...
- Linux系统之路Centos7.2——安装QQ 的一些问题(附VMware的安装)
1.首先安装wine 可以通过源码安装,注意在编译的时候加参数,编译64位(如果你的系统是64位哦!) 但是我建议直接rpm安装. 安装网络源: rpm -ivh epel-release-6-8.n ...
- Java入门:Java下载与安装方法
本文适合刚入门的Java编程的初学者阅读. JDK有两种下载方法,一个是官网下载,另一个是第三方网站下载.官网速度也许有点慢,慢的话可以考虑去第三方网站下载. 一.官网下载 1. 访问地址:http: ...
- NATS_10:NATS服务配置文件详解
尽管NATS可以无配置的运行,但也可以使用配置文件配置NATS服务的启动,在实际应用当中,一般都是通过使用配置文件来启动服务的. 1. 配置项包括 客户端监听器端口 Client listening ...
- oracle进阶之分析函数
本博客是自己在学习和工作途中的积累与总结,纯属经验之谈,仅供自己参考,也欢迎大家转载,转载时请注明出处. http://www.cnblogs.com/king-xg/p/6797119.html 分 ...
- UITableViewCell在非Nib及Cell重用下设置CellStyle
在UITableViewController(实现了UITableViewDataSource)下需要实现 - (UITableViewCell *)tableView:(UITableView *) ...
- webapi框架搭建-安全机制(三)-简单的基于角色的权限控制
webapi框架搭建系列博客 上一篇已经完成了“身份验证”,如果只是想简单的实现基于角色的权限管理,我们基本上不用写代码,微软已经提供了authorize特性,直接用就行. Authorize特性的使 ...