声明是C语言中一个非常基础但重要的部分,无论是阅读他人的代码,还是排查编译报错,正确理解声明都会对我们有莫大的帮助。
有的人可能会说声明不是很简单吗?
小A说,看,我声明了一个整型变量:
int a;
小B说,瞧,我声明了一个函数:
int func(void);
我想大家理解上面的声明肯定是手拿把掐,轻而易举。
那么,下面几个声明呢?
int* arr[10];
int (*arr_ptr)[10];
int *func(int a);
int (*func_ptr)(int a);
以及这个呢?
int (*func_arr[5])(int a, int b);
如果你有点拿不准了,那就往下看吧,本文的目的就是给大家分享一下解析声明的方法,从而使得各位更好地理解 C 语言中的声明。
一、认识声明
首先让我们来看下声明的组成,一般来说,声明具体如下格式:
声明说明符 声明符;
声明说明符描述的是声明的数据项的性质,声明符描述的则是数据项的名字,并且描述了数据项性质的额外特点。
声明说明符有三大类:
- 存储类型:
auto,static,extern,register - 类型限定符:
const,volatile - 类型说明符:
void,char,short,int…
为了让大家便于理解,本文只讨论仅带类型说明符的声明,我想这是大家最熟悉的部分,存储类型与类型限定符我们以后再讨论。
接下来,让我们来认识一下声明符:声明符是由标识符、*、[]、()组成的。
-
标识符:声明的变量或者函数的名字
int a;这是最简单的声明符,其中a是标识符,由于没有其他符号,因此这个标识符a就是声明符。
-
*:如果在标识符前加一个*号,那么这个声明符表示指针int *p; -
[]:如果在标识符后加一个[],那么这个声明符表示数组int arr[10]; -
(): 如果在标识符后加一个(),那么这个声明符表示函数int add(int a, int b);
上面列出的几个声明符都很简单,但是我们在实际工作中遇到的声明符往往是标识符、*、[]、()的组合,也就是复杂声明符。接下来,让我们来看看如何分析复杂声明符。
二、声明的解析规则
有两条简单的规则可以帮助我们理解所有的声明,好的,划重点了:
第一条规则:由内而外
这条规则的含义就是,首先定位声明中的标识符,并由此开始解析
第二条规则:遇事不决找括号
当我们运用第一条规则找到标识符后,我们往往会发现标识符的左右两边有其他符号,这时候我们就会面临一个选择:向左走?向右走?
第二条规则为我们指明了方向,那就是找括号,即先
()和[],再*
举个栗子,让我们来分析如下声明:
int* arr[10];
根据第一条规则,我们首先找到标识符arr,然后从此处开始解析。
我们观察到在arr的左边是*,右边则是[10],那么arr是指针还是数组呢?
让我们运用第二条规则,先将arr与[10]进行组合,这么一来**,arr的性质就确定了,它是一个数组**。
既然它是一个数组,那么就需要说明数组元素的类型和数量,数量我们已经知道了,10个,那么数组元素是什么类型呢?
根据第二条规则,接下来要解析*了,这说明数组元素的类型是指针,那么是什么类型的指针呢?int。因此,我们知道了数组元素的类型是整型指针。
综合起来,arr是一个包含10个元素的数组,每个元素的都是整型指针。
接着,我们来解析这条声明:
int *func(int a);
同样的,首先找到标识符,func,然后观察func的两边,发现左边是*,右边是(int a)。
那么根据第二条规则,我们应该先将func与(int a)结合,如此一来,func的性质就确定了,它是一个函数。
既然是函数,那就要有参数和返回类型,根据(int a)我们可以知道,这个函数的接受一个整型参数。
对于返回类型,我们可以通过解析*得知,该函数的返回类型是一个指针,而指针的类型则是int,即整型指针。
综合起来,func是一个函数,它接收一个整型参数,返回类型是整型指针。
三、攻克复杂声明符
大家估计看出来了,第二条规则其实就是优先级的运用,因为()与[]的优先级比*更高,所以我们先解析()和[],再解析*。
但是我们知道,()可以改变优先级,比如下面的声明:
int (*func_ptr)(int a, int b);
在上面的声明中,标识符func_ptr的左边是*,右边是(int a, int b),按照规则,应该先将func_ptr与(int a, int b)结合,从而将func_ptr定性为函数。
但是,func_ptr与*被()包了起来,因此优先级被提高了,所以我们要先将func_ptr与*号进行结合,因此func_ptr应该被定性为指针。
接着,我们要解析(int a, int b),从而可以确定func_ptr指向的是一个接受两个整型参数的函数,最后根据开头的int确定函数的返回值为整型。
综合起来就是,func_ptr是一个指向函数的指针,该函数接受两个整型参数的函数,返回类型是整型。
这种指针,我们一般叫它函数指针。
好了,有了以上经验,我们可以来解析本文的最终Boss了:
int (*func_arr[5])(int a, int b);
解析步骤如下:
- 找到标识符
func_arr func_arr左边是*,右边是[5],因此先将func_arr与[5]结合,从而将func_arr定性为数组,元素个数为5func_arr[5]的左边是*,右边是(int a, int b),但是func_arr[5]与*被()包起来了,所以先解析*,从而确定数组的元素的类型是指针- 接下来解析
(int a, int b),可以确定指针指向的是一个接收两个整型参数的函数 - 最后根据开头的
int确定函数的返回类型是整型
综合起来,func_arr是一个包含5个元素的数组,每个数组元素都是函数指针,指向的函数接受两个整型参数,返回类型是整型。
结语
希望通过本文的实例分析,可以帮助大家更好地理解和应用 C 语言中的各种声明语句。
若有错漏之处,欢迎大家交流指正~
本文首发于微信公众号《Linux在秋名山》,欢迎大家关注~
参考文献
[1] K.N.King, C语言程序设计:现代方法(第2版), 吕秀锋, 黄倩译,人民邮电出版社, 2010.

1347

被折叠的 条评论
为什么被折叠?



