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 ...
随机推荐
- BZOJ1495 [NOI2006]网络收费 【树形dp + 状压dp】
题目链接 BZOJ1495 题解 观察表格,实际上就是分\(A\)多和\(B\)两种情况,分别对应每个点选\(A\)权值或者\(B\)权值,所以成对的权值可以分到每个点上 所以每个非叶节点实际对应一个 ...
- Centos Python3安装共存
安装python3.6可能使用的依赖 yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlit ...
- 网络中,FIFO、LRU、OPT这三种置换算法的缺页次数
FIFO.LRU.OPT这三种置换算法的缺页次数 转载 由于要考计算机四级网络,这里遇到了问题,就搜了一些资料来解疑. 考虑下述页面走向: 1,2,3,4,2,1,5,6,2,1,2,3,7,6,3 ...
- ppp协议介绍(转)
原文:https://www.cnblogs.com/gtarcoder/p/6259105.html PPP协议PPP协议是二层(数据链路层)协议,常用于拨号上网时客户端向服务器获取IP地址.PPP ...
- ubuntu vim 配置
set nuset autoindent cindentmap<F9> :w<cr> :!g++ -O2 -o %< % -Wall<cr>map<F1 ...
- Java入门:用户登录与注册模块1(实践项目)——分析
任务描述:用户登录与注册是大多数软件都拥有的一个模块.请编写一个控制台程序,实现用户的登录与注册功能,并且用户能够修改自己信息. [需求分析]由于本程序是一个演示程序,用户的信息我们做简化处理,仅包括 ...
- Linux最大文件句柄(文件描述符)限制和修改
转自:http://jameswxx.iteye.com/blog/2096461 写这个文章是为了以正视听,网上的文章人云亦云到简直令人发指.到底最大文件数被什么限制了?too many open ...
- python 常用模块之os
1.权限判断 bool: os.access('/python/test.py',os.F_OK) #是否存在 bool: os.access('/python/test.py',os.R_OK) # ...
- go语言从零学起(三) -- chat实现的思考
要通过go实现一个应用场景: 1 建立一个websocket服务 2 维护在线用户的链接 3 推送消息和接受用户的操作 列出需求,很显然的想到了chat模型.于是研究了revel框架提供的sample ...
- Java基础-正则表达式(Regular Expression)语法规则简介
Java基础-正则表达式(Regular Expression)语法规则简介 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.正则表达式的概念 正则表达式(Regular Exp ...