理解C语言刁钻定义语句的斩麻快刀:自内向外读

本文介绍了理解C语言复杂定义语句的一种方法——自内向外读,通过多个示例详细阐述了该方法的应用,包括指针、数组、函数、typedef等,并提到了typedef在定义结构和联合时的作用。
摘要由CSDN通过智能技术生成

理解C语言刁钻定义语句的斩麻快刀:自内向外读

 

今天在CSDN论坛见到 A_Zhao 发表于2012-06-18 06:41:16关于C语言声明、指针、数组、函数、typedef...》引起论坛推荐和朋友们的热议。A_Zhao文章很长,因为健康欠佳,力不从心,我大概看了一下,没怎么看懂。我感觉他的方法不容易,我知难而退了。

其实早有书籍讲解过这个问题,方法很简单,看来朋友们没看到这类的书。至于我是从哪本书看到的,可想不起来了,大概有10年之久了吧。

这个方法的基本原则是:

1.  从标识符开始读起,即从内向外读。

2.  左右都有类型符时,读的次序按照它们是运算符时的级别决定。

3.  用后面步骤读到的类型符去解释前面步骤新出现的那个类型符(陈树振原创)。

依据这个原则,任何复杂的定义语句都能迎刃而解,相信吗?咱们先从简单的练起,逐步加难。复杂的例子我准备参考A_Zhao朋友的题材,也好做个对比

 

1char *a[3]; 

这是一个定义语句,因为类型名开头。从标识符a读起,a的左右各有一个类型符,分别是*[ ],二者做运算符时取下标运算符[ ]高于取内容运算符*,所以先读[ ]。即:1a是一个有3个元素的数组;2。数组的元素是指针;3。这个指针是指向char型变量的。

这里有个前提,大家要理解。在C语言里,同一个符号出现在不同点环境(context)其意义是不一样的,这和人类的自然语言是一样的。例如这里的*[ ]就既可以是类型符又可以是运算符。这里再啰嗦一句吧,出现在定义语句里只能是类型符,而不可能是运算符。类型符没有先后级别,而运算符有,这里是借用。

 

2char*(foo())[3];

我们从标识符出发开始读。1。先读小括号, foo是一个函数(函数就要有返值类型)2。函数返值的类型是数组(这是因为取下标运算符[ ]高于取内容运算符*...。到这里就有错了 ,因为函数的返值是不允许是数组的。这个定义语句是错误的。

我们看到,这个定义语句去掉一个小括号,即char* foo()[3];,与原定义语句的含义保持不变。为什么?因为取下标运算符[ ]高于取内容运算符*,对吧?

 

3char*(*(*ptr2bar)(int *))[3];

我们从标识符出发开始读。1。先读小括号(*ptr2bar) ptr2bar是一个指针2。再读(*ptr2bar)(int*),是一个指向带有一个int *参数的函数的指针 3。再读(*(*ptr2bar)(int *))[3],这个函数的返值是指向3个元素的一维数组的指针4。最后读作,这3个元素的数据类型是char*

    定义语句char* (*(*ptr2bar)(int *))[3];里的小括号一个都不能省略,否则含义就变了。

看出规律了吗?后面步骤读到的类型符去解释前面步骤新出现的那个类型符。我说这话有点难懂,不过您掌握这个窍门,多练习几次就理解我说的话了。

 

下面我要给大家介绍使用typedef的小窍门,这可是我自己琢磨出来的,有专利权。

大家知道,typedef的功能是给一种类型起一个别名,typedef实际上不能产生任何新的数据类型。我们在MFC类库及其应用程序框架里经常见到的LPSTRDWORDWPARAMLPARAM都是用typedef起的别名,原来的名称其实我们在学C语言时基本上都见过,没什么新东西。

通常对一种复杂的、组合的数据类型才会起一个新名字,这样会使得程序见名知意。但这时初学者往往分不清哪个是别名的含义。我的窍门是:

1.  先去掉看关键字typedef,那么此语句一定是一个定义语句,找出所定义的变量或数

2.  加上关键字typedef,这个变量或数组名就成为类型名了,其数据类型含义就是刚才作变量时的那个含义。

3.  用这个类型名去定义变量或数组。

 

4

我们把前面例1加上typedef 就成为:

typedef char *a[3];

现在a就是一个类型名了。用a去定义一个数组array,即:

a array;

现在array就是一个数组,一个有3个元素的数组,每个元素是指向char型的指针。

 

其实typedef更常用来定义结构或联合。

5typedef struct {

         int numNo;

         unsigned int age;

         char sex;

          intexam[5];

    }STUDENT;

     STUDENTzhang3;

1.先去掉typedefSTUDENT是结构变量名;2。加上typedefSTUDENT就是类型名了。

 

下面来一个比较复杂点的,从侯捷的《深入浅出MFC》的Frame8源程序里获得。

6

typedef void (CCmd::*AFX_PMSG) (void);

1.先把typedef去掉;2。从标识符AFX_PMSG开始向外读,AFX_PMSG是一个指针;3。这个指针是指向CCmd类成员的指针;4。这个指针是指向无参函数的指针(当然是成员函数);5。函数的返值是void6。加上typedef,上面的AFX_PMSG就成了一个类型,一个具有刚才作变量时属性的数据类型。

 

据说,从内向外读的原则是与编译器对定义语句的语义分析原则是一致的。这个原则对于理解C++出现在定义语句的修饰符也非常重要。见如下例子:

 

7:对下面第二条定义语句:

int  var=5;

const  int  *p;

p=&var;          

   

我们从标识符开始从内向外读:1。先读p,读做:“p是一个标识符”;2。再读类型符*,读做:“是一个指针变量”;3。再读类型符int,读做:“它指向int类型的变量”;④再读修饰符const,读做:“这个int型变量不能通过指针p去改变它的值”。

在上面的程序段里,我们可以通过var去改写上述写有整型数5的那小块内存,如var=6;;但是,不能通过指针p去改变写有整型数5的那小块内存。

 

8:再如,可以如下定义一个指针p

int  var=5;

int  * const  p=&var;

 

我们这样读第二个定义语句:从标识符开始读起,从内向外读,1。先读p,读作:“p是一个标识符”;2。再读修饰符const, 读作:“pconst 3。再读类型符*,读作:“p是一个指针变量”4。再读类型符int, 读作:“p指向int类型的变量”;⑤再读=号,p被初始化为&var

                                  陈树振写于 2012/6/19

我的信箱是:chenshuzhenteacher@126.com  欢迎联系。

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值