《征服 C 指针》摘录6:解读 C 的声明
一、混乱的声明——如何自然地理解 C 的声明?
通常,C 的声明
int hoge;
这样,使用“类型 变量名;”的形式进行书写。
可是,像“指向 int 的指针”类型的变量,却要像下面这样进行声明:
int *hoge_p;
似乎这里声明了一个名为 *hoge_p 的变量,而实际上,这里声明的变量是 hoge_p,hoge_p 的类型是“指向 int 的指针”。
因为这种声明方式不太好理解,所以有人提出将 * 靠近类型这一侧进行书写,如下:
int* hoge_p;
的确,这种书写方式符号“类型 变量名;”的形式。但是在同时声明多个变量的情况下就会出现破绽:
/* 声明 2 个“指向 int 的指针”? ——其实不是,此时 piyo_p 是 int 类型变量 */
int* hoge_p, piyo_p;
此外,数组也是 C 的一种类型,比如
int hoge[10];
这样的写法,就不符合“类型 变量名;”的形式。
说一些题外话,Java 在声明“int 的数组”时,通常写成
int[] hoge;
的形式,这样好像是符合“类型 变量名;”的形式。至少在这一点上,Java 的语法比起 C 显得更为合理。可是,Java 为了让 C 程序员更容易地将程序向 Java 移植,竟然也兼容 int hoge[] 这样的写法。这种不伦不类的做法倒还真像Java的风格。
二、用英语来阅读
我认为像 int *hoge_p; 还有 int hoge[10]; 这样的声明方式很奇怪。
对于这种程序的声明方式,可能也有很多人感觉不到什么别扭的地方。那就再看下面的这个例子(经常被使用):
char *color_name[] = {
"red",
"green",
"blue"
};
这里声明了一个“指向 char 的指针的数组”。
我们还可以像下面这样声明一个“指向将 double z作为参数并且返回 int 的函数的指针”:
int (*func_p)(double);
关于这样的声明,在 K&R 中有下面这样一段说明:
int *f(); /* f: 返回指向 int 指针的函数 */
和
int (*pf)(); /* pf: 指向返回 int 的函数的指针 */
这两个声明最能说明问题。在这里,因为 * 是前置运算符,它的优先级低于 (),为了让连接正确地进行,有必要加上括号。
首先,这段文字中有谎言。
声明中 *、() 和 [] 并不是运算符。在语法规则中,运算符的优先顺序是在别的地方定义的。
先将这个问题放在一边。如果你老老实实地去读这段文字,该会嘀咕“是不是搞反了”。如果说
int (*pf)();
是指向函数的指针,使用括弧先将星号(指针)括起来岂不是很奇怪?
这个问题的答案,等你明白过来就会觉得非常简单。C 语言本来是美国人开发的,最好还是用英语来读。
以上的声明,如果从 pf 开始以英语的顺序来读,应该是下面这样:
pf is pointer to function returning int
翻译诚中文,则为
pf 为指向返回 int 的函数的指针。
要点
用英语来读 C 的声明。
三、解读 C 的声明
在这里,向读者介绍阅读 C 语言声明的方法:机械地向前读。
为了把问题变得更简单,我们在这里不考虑 const 和 volatile。接下来遵循以下步骤来解释 C 的声明。
1、首先着眼与标识符(变量名 或者 函数名)。
2、从距离标识符最近的地方开始,依照优先顺序解释派生类型(指针、数组 和 函数)。优先顺序说明如下,
(1)、用于整理声明内容的括弧
(2)、用于表示数组的[],用于表示函数的()
(3)、用于表示指针的 *
3、解释完成派生类型,使用 “of”,“to”,“returning”将它们连接起来。
4、最后,追加数据类型修饰符(在左边,int、double 等)。
5、英语不好的人,可以倒序用中文解释。
数组元素个数和函数的参数属于类型的一部分。应该将它们作为附属于类型的属性进行解释。
比如,
int (*func_p)(double);
(1) 首先着眼于标识符。
int (*func_p)(double)
英语的表达为:
func_p is
(2) 因为存在括号,这里着眼于 *。
int (*func_p)(double);
英语的表达为:
func_p is pointer to
(3) 解释用于函数的(),参数 double。
int (*func_p)(double);
英语的表达式为:
func_p is pointer to function(double) returning
(4) 最后,解释数据类型修饰符 int。
int (*func_p)(double);
英语的表达为:
func_p is pointer to function(double) returning int
(5) 翻译成中文:
func_p 是指向返回 int 的函数的指针。
使用和上面相同的方式,我们对各种各样的声明进行解读,如下表所示:
C 语言 | 英语表达 | 中文表达 |
---|---|---|
int hoge; | hoge is int | hoge 是int |
int hoge[10]; | hoge is array(元素个数10) of int | hoge 是 int 的数组(元素个数10) |
int hoge[10][3]; | hoge is array(元素个数10) of array(元素个数3) of int | hoge 是 int 数组(元素个数3)的数组(元素个数10) |
int *hoge[10]; | hoge is array(元素个数10) of pointer to int | hoge 是指向 int 的指针的数组(元素个数 10) |
double (*hoge)[3]; | hoge is pointer to array(元素个数3) of double | hoge 是指向 double 的数组(元素个数3)的指针 |
int func(int a); | func is function(参数为 int a) returning int | func 是返回 int 的函数(参数是 int a) |
int (*func_p)(int a) | func_p is pointer to function(参数为 int a) returning int | func_p 是指向返回 int 的函数(参数为 int a)的指针 |
正如大家看到的这样,C 语言的声明不能从左右按顺序解读(无论是英语、中文、还是日语),而是左右来回地解读。
K&R 中指出:在 C 语言中,变量的声明仿效表达式的语法。可是,勉强地去模拟本质上完全不同的事物,结果就是“四不像”。
“使声明的形式 和 使用的形式相似”是 C (还有从 C 派生的 C++、Java 等语言)特有的 奇怪的语法。
K&R 中同时也写道:
C 的声明语法,特别是指向函数指针的语法,受到了严厉的批评。
四、类型名
在 C 中,除标识符以外,有时候还必须定义“类型”。具体来说,遇到以下情况需定义“类型”:
(1)、在强制转型运算符中
(2)、类型作为 sizeof 运算符的操作数
比如,将强制转型运算符写成下面的形式:
(int *)
这里指定“int *”为类型名。
从标识符的声明中,将标识符取出后,剩下的部分自然就是类型名。
如下表所示,类型名的写法:
声 明 | 声明的解释 | 类型名 | 类型名的解释 |
---|---|---|---|
int hoge; | hoge 是 int | int | int 类型 |
int *hoge; | hoge 是指向 int 的指针 | int * | 指向 int 的指针类型 |
double (*p)[3]; | p 是指向 double 的数组(元素个数 3)的指针 | double(*)[3] | 指向 double 的数组(元素个数 3)的指针类型 |
void (*func)(); | func 是指向返回 void 函数的指针 | void (*)() | 指向返回 void 函数的指针类型 |
上表最后2个例子中,括起星号的括弧 (*) 好像有点多余,但是一旦去掉括弧,意思就完全不一样了。
1、(double *[3]) 是将 double *hoge[3] 的标识符去掉后形成的,所以这个类型名被解释成“指向 double 的指针的数组”。
2、int (*func_table[10])(int a) 可以解释成“指向返回 int 的函数(参数为 int a)的指针的数组(元素个数 10)”。
延伸阅读:
《征服 C 指针》摘录1:什么是空指针?区分 NULL、0 和 '\0'
《征服 C 指针》摘录2:C变量的 作用域 和 生命周期(存储期)
《征服 C 指针》摘录6:解读 C 的声明的更多相关文章
- 《征服 C 指针》摘录1:什么是空指针?区分 NULL、0 和 '\0'
一.什么是空指针? 空指针 是一个特殊的指针值. 空指针 是指可以确保没有向任何一个对象的指针.通常使用宏定义 NULL 来表示空指针常量值. 空指针 确保它和任何非空指针进行比较都不会相等,因此经常 ...
- 《征服 C 指针》摘录2:C变量的 作用域 和 生命周期(存储期)
在开发一些小程序的时候,也许我们并不在意作用域的必要性.可是,当你书写几万行,甚至几十万行的代码的时候,没有作用域肯定是不能忍受的. C 语言有如下 3 种作用域. 1.全局变量 在函数之外声明的变量 ...
- 《征服 C 指针》摘录3:数组 与 指针
一.数组 和 指针 的微妙关系 数组 是指将固定个数.相同类型的变量排列起来的对象. 正如之前说明的那样,给指针加 N,指针前进“当前指针指向的变量类型的长度 X N”. 因此,给指向数组的某个元素的 ...
- 《征服 C 指针》摘录4:函数 与 指针
一.指向函数的指针 函数名可以在表达式中被解读成“指向函数的指针”,因此,正如代码清单 2-2 的实验那样,写成 func 就可以取得指向函数的指针. “指向函数的指针”本质上也是指针(地址),所以可 ...
- 《征服 C 指针》摘录5:函数形参 和 空的下标运算符[]
一.函数的形参的声明 C 语言可以像下面这样声明函数的形参: void func(int a[]) { // ... } 对于这种写法,无论怎么看都好像要向函数的参数传递数组. 可是,在 C ...
- 《征服 C 指针》笔记6:练习——挑战那些复杂的声明
应该是小试牛刀的时候了. 在 ANSI C 的标准库中,有一个 atexit()函数.如果使用这个函数,当程序正常结束的时候,可以回调一个指定的函数. atexit()的原型定义如下: int ate ...
- 《征服C指针》读书笔记
本文同时发布在我的个人博客上,欢迎访问~ www.seekingdream.cn 在读完K&R之后,对C的认识就是指针.数组.网上的人们对指针也有些“敬而远之”的感觉.最近从同学处淘得< ...
- C++指针的概念解读
C++指针的概念解读 超详细 指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址.要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区 ...
- 《征服c指针》学习笔记-----统计文本单词数目的程序word_count
1.程序的要求:对用户指定的英文文本文件(包括标准输入),将英文单词按照字母顺序输出到用户指定的文本文件中(包括标准输出),并且在各单词后面显示单词的出现次数. 2.模块设计: 主要分为:1.从输入流 ...
随机推荐
- Ural 1010. Discrete Function
1010. Discrete Function Time limit: 1.0 secondMemory limit: 64 MB There is a discrete function. It i ...
- CH Round #72树洞[二分答案 DFS&&BFS]
树洞 CH Round #72 - NOIP夏季划水赛 描述 在一片栖息地上有N棵树,每棵树下住着一只兔子,有M条路径连接这些树.更特殊地是,只有一棵树有3条或更多的路径与它相连,其它的树只有1条或2 ...
- 5.bootstrap练习笔记-巨幕和流体布局
bootstrap练习笔记-巨幕和流体布局 1.在bootstrap中 .jumbotron可以设置巨幕效果 2.div.jumnotron自动设置一个黑色的巨幕效果 3.div.container ...
- 4.bootstrap练习笔记-内容区块
bootstrap练习笔记-内容区块 1.bootstrap中,采用的全部是div布局,把你的内容首先要包含在一个大的DIV区块当中 2.然后再写一个div.container,这个div里面存放真正 ...
- css只显示两行 超过显示点点点
.p1{ text-align: center; color:#C8CFD6; height:34px; text-overflow:-o-ellipsis-lastline; overflow:hi ...
- 系统修改利器XueTr
Windows系统修改利器XueTr 周银辉 在Windows下如果遇到某些进程弄死结束不了,某些文件弄死删不掉,拷贝不出来 (可能是因为你没有管理员权限,可能是因为人家是病毒,可能是系统保护文件,可 ...
- [转有改动]vi
转自http://www.51testing.com/html/86/427686-247344.html 多按几次[ESC],系统会发出滴滴声以确定进入命令模式.就进入了命令模式,所有在键盘上打的字 ...
- 让所有的浏览器都能识别HTML5标签样式的小插件
如今HTML5愈来愈引发大家的关注了,但目前支持HTML5的浏览器还不是主流,特别是国内用户近50%以上仍旧使用IE6,由于支持HTML5的IE9不支持Xp系统安装,这样未来很长一段时间,HTML5的 ...
- Office2013插件开发Outlook篇(2)-- Ribbon
一.获取当前实例 在Ribbon1的任何方法中调用如下代码,可获取当前实例. 如: Application application = new Application(); var list = ap ...
- 关于DOS与cmd(windows系统)
dos是计算机的最初期的操作系统,对电脑操作必须输入各种dos命令窗口,可以理解成运行计算机机器内部语言,知道编程吗?其实早期dos命令操作系统就是运行计算机内部的编程命令,因此操作人员都必须具有一定 ...