上面的代码声明了 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[]) { /* ... */ }
尽管看起来有些不同,但是基本的结构是一样的 。
总的来看 , 当类型比较简单时,C的语法显得很聪明 。但是遗憾的是一旦类型开始复杂,C的这套语法很快就能让人迷糊了 。著名的例子如函数指针,我们得按下面这样来写:
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 在做类型转换的时候总是要把类型用括号括起来的原因,像这样
(int)M_PI
Go 的语法
非C家族的语言通常在声明时使用一种不同的类型语法 。一般是名字先出现,然后常常跟着一个冒号 。按照这样来写,我们上面所举的例子就会变成下面这样:
x: int
p: pointer to int
a: array[3] of int
这样的声明即便有些冗长,当至少是清晰的——你只需从左向右读就行 。Go 语言所采用的方案就是以此为基础的,但为了追求简洁性,Go 语言丢掉了冒号并去掉了部分关键词,成了下面这样:
x int
p *int
a [3]int
在 [3]int 和表达式中 a 的用法没有直接的对应关系(我们在下一节会回过头来探讨指针的问题) 。至此 , 你获得了代码清晰性方面的提升,但付出的代价是语法上需要区别对待 。
下面我们来考虑函数的问题 。虽然在 Go 语言里,main 函数实际上没有参数 , 但是我们先誊抄一下之前的 main 函数的声明:
推荐阅读
- 斗鱼直播绑定不了手机,斗鱼为什么绑定了手机还要求绑定
- c链接mysql的字符串,c连接mysql
- 赛车游戏双显卡,赛车游戏双屏
- 什么网络射击游戏,什么网络射击游戏好玩
- go语言没有泛型,go语言为什么没有类
- 电商目前如何,电商前景如何电商行业的发展前景
- mongodb网址是多少,mongodb mongos
- python感知机函数 感知机使用_______________函数作为其激励函数
- 显卡里面没有说明书怎么办,显卡没有说明书吗