C语言基本知识之------声明与定义
声明与定义
以前学C语言的时候,你有木有为下面的类似声明而迷惑呢:
const int *p;
int const *p;
int * const p;
这里的const到底是修饰int,还是int*呢? 对于这几个声明的理解,是一些公司偏重的面试题。曾经我也很努力地去记住每一个声明的具体意义,但是过了没多久就又全忘了。最近看了《C专家编程》一书中的解释,才晃然间感觉到是真的有些懂了。
那么什么是声明,什么是定义呢?
首先我们先想一下,什么是声明,什么是定义,为什么会有这两个东西在C语言中出现?
我个人觉得,在现实世界中,我们可以把声明理解为一种告示。声明就是告诉别人说,这里有这么一个东西是属于谁的或者是什么样的一件事情等等。当然这个告示你可以贴的到处都是,以防有的人会看不到。因此,声明可以有多份。
类似地,在C语言的世界里,声明一个变量,就是通告说在某个地方有这么一个变量存在,我们了解了地方有之后,就可以拿来使用了。
定义说白了就是为某个东西取一个统一的意思,它也是一种特殊的声明。在C语言里,定义就是一种特殊的声明,定义一个变量的时候编译器会为其分配内存。
因此,在C语言中同一变量可以被声明多次,但只能被定义一次(因为定义的时候分配内存,内存只分配一次就够了)。
怎么解析变量的声明?
既然有了声明,那么想一下,声明在编译器中又是怎样被解析的呢?我们写的任何程序都是用来执行的,要执行程序,编译器肯定就要先理解程序。只要我们知道计算机是怎么来理解这些语言的,我们也就能很轻易的理解这些相关的知识了。
像上面提到的三个声明:
const int *p;
int const *p;
int * const p;
编译器会怎样理解呢?它会怎么定位这三个p的具体意义呢?当然,编译器是木有思想的,它只会一步步的去执行,所以编译器的开发者肯定会为它制定一个规则,告诉它说怎样怎样去一步步的理解程序中的声明。并且,这个规则必须是无二义性的,要不然编译器就会迷茫而不知道应该去选择那个意思去理解了。
想一想,如没有一个指定的规则,而让编译器随便去解析就好的话,那么我们的同一段程序就有可能会有很多不同的结果。这样一来,计算机的世界其不是乱了套了。
因此,只要我们抓着编译器的理解规则,就能轻而易举地现理解声明的具体意义了。
关于C的声明,编译器的解析规则是这样的:
[
编译器,从最左边开始读取,碰到第一个标识符就认定这是我们要定义的变量,接着它就按照如下的优先级依次读取并解析声明:
p1, 如果当前读到的标识符被括号括了起来,它就先解析括起来的那部分
p2, 解析完括号里的内容,它会向后看一眼,看看后面有什么东西不。
如果后面碰到[],就解析为数组;如果后面碰到()就解析为函数
p3,看完后面,就向前看一眼,如果前面有*就说明是指针。
另外,编译器规定,const/volatile 一般修饰其后面紧跟的类型说明符,否则它作用于其左边的符号。
]
举几个例子,来模仿下编译器的解析过程:
例1, char * const * (* next) ();
1, char * const * (* next) (); 第一步,编译器读取到左边的第一个标识符next, 表示定义一变量next
2, char * const * (* ) (); 第二步,解析标识符被括起来的部分:同样按照优先级规则,先看一下next后面,木有东西;然后看一下next的前面,有一个*。说明 next是个指针。
3,char * const * (); 第三步,看完括号中的部分,看一下后面,有()说明,next指向的是一个函数。
4,char * const * ; 第四步,看完后面,看一下前面*,说明函数的返回值是一个指针。
5,char * const ; 第五步,解析const, 按照规定,const修饰它后面紧跟的类型说明符,此处const后面没有类型说明符。所以它修饰其左边的 *, 说明是常指针
6, char * ; 第六步,char *,说明是指向字符的指针。
总结以上的解析过程可得,next是一个指针,它指向函数,函数的返回值是一指针,指向一个常指针,常指针指向的内容是字符。简言之,next是一个指向函数的指针,函数返回指向字符常指针的指针。
例2,文章刚开始处提到的三个声明
按照编译器规定,
const int * p; 因为const 后面紧挨着int, 所以它修饰int,说明是常整型。这里p是指向const int的指针;
int const * p; 因为const 后面紧跟的不是类型说明符,所以它作用于其左边的int,因此这里的解析为p是指向const int的指针;
int * const p; 因为const 后面紧跟的不是类型说明符,所以它作用于其左边的int *,因此这里的解析为p是const 指针,它指向int类型的变量。
总结
知道了编译器的解析规则,以后碰到各种声明我们都可以自己按照编译器的思路去解析了,而不用再死记硬背每一个声明具体代表什么意思。因此,知其然并知其所以然才是王道。
本人在这里自己写出来,主要是为了巩固自己的知识理解。如果有人对这块知识点感兴趣 建议大家还是去看看《C专家编程》的第三章分析C语言的声明或研究下C语言声明解析的源代码。相信在哪里你会得到更好的解释,通过别人转达过来的知识,终归会有些变味地。
参考文献:
1,《C专家编程》------第三章 分析C语言的声明
2,推荐一个关于C声明的解析网站:www.cdecl.org
从这个网站上我们还可以下载c语言声明的解析代码,有兴趣的可以研究下。