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 ...
随机推荐
- Vue设置不同的环境发布程序
原文地址: http://www.cnblogs.com/JimmyBright/p/7307486.html 通常应用程序上线都会经过开发环境.测试环境.生产环境三个阶段,三个环境通常会对应有三个不 ...
- Java的顺序栈和链式栈
栈的定义 栈是限制在表的一段进行插入和删除的运算的线性表,通常能够将插入.删除的一端为栈顶,例外一端称为栈底,当表中没有任何元素的时候称为空栈. 通常删除(又称"退栈")叫做弹出p ...
- 【CF888G】Xor-MST(最小生成树,Trie树)
[CF888G]Xor-MST(最小生成树,Trie树) 题面 CF 洛谷 题解 利用\(Kruskal\)或者\(Prim\)算法都很不好计算. 然而我们还有一个叫啥来着?\(B\)啥啥的算法,就叫 ...
- linux内核分析(网课期末&地面课期中)
堆栈变化过程: Linux内核分析——计算机是如何工作的 计算机是如何工作的?(总结)——三个法宝 存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: 函数调用堆栈,高级语言得以运行的基础,只有 ...
- 【agc019C】Fountain Walk
Portal --> agc019C Description 有一个\(10^8*10^8\)的网格图,一格距离为\(100\),第\(x\)条竖线和第\(y\)条横线的交点记为\((x,y)\ ...
- echarts分组柱状图的前后台处理 带平均线显示
原生的echarts使用: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /&g ...
- PDF文档小技巧整理一览
1.福昕阅读器文档背景修改为保护眼睛的颜色? 1)文件 -> 偏好设置 -> 访问 -> 勾选 "改变文档颜色" 2)选择 '自定义颜色'->'页面背景颜色 ...
- mysql 优化总结
1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. (laravel 可以使用 debugbar 包,可以及时发现低效 sql 语句,不使用索 ...
- 利用ansible来做tomcat应用的持续交付
https://www.jianshu.com/p/fca8f91ae223 在做持续交付这件事,想必大家都是用jenkins这款程序来做基石.当然,我们这次也是用jenkins作为承载工具,jenk ...
- Maven学习一:使用Myeclipse创建Maven项目
使用Myeclipse2014创建Maven项目有如下几种方式: 1.创建Maven Java项目 1.1 选择新建Maven项目 1.2.选择创建简单项目 1.3.填写项目信息 1.4.创建成功后项 ...