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

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

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

这个方法的基本原则是:

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

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

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

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

例1:char *a[3];

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

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

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

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

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

例3:char*(*(*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类库及其应用程序框架里经常见到的LPSTR、DWORD、WPARAM、LPARAM都是用typedef起的别名,原来的名称其实我们在学C语言时基本上都见过,没什么新东西。

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

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

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

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

例4:

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

typedef char *a[3];

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

a array;

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

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

例5:typedef struct {

int numNo;

unsigned int age;

char sex;

intexam[5];

}STUDENT;

STUDENTzhang3;

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

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

例6:

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

1.先把typedef去掉;2。从标识符AFX_PMSG开始向外读,AFX_PMSG是一个指针;3。这个指针是指向CCmd类成员的指针;4。这个指针是指向无参函数的指针(当然是成员函数);5。函数的返值是void;6。加上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, 读作:“p被const” ;3。再读类型符*,读作:“p是一个指针变量”;4。再读类型符int, 读作:“p指向int类型的变量”;⑤再读=号,p被初始化为&var。

陈树振写于 2012/6/19

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

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

  1. Database之SQLSever:T-SQL数据语言操作(数据定义语句DDL、数据操作语句DML、数据控制语句DCL、其他基本语句、流程控制语句、批处理语句)概念及其相关案例之详细攻略

    Database之SQLSever:T-SQL数据语言操作(数据定义语句DDL.数据操作语句DML.数据控制语句DCL.其他基本语句.流程控制语句.批处理语句)概念及其相关案例之详细攻略 目录 T-S ...

  2. C语言递归调用return语句,理解C语言递归函数的逐级返回(return)

    递归函数,也即调用自身的函数. C Primer Plus中有个例子很棒: /*理解C语言递归函数*/ #include void up_and_down(int); int  main(void) ...

  3. 深入理解C语言的函数调用过程

    深入理解C语言的函数调用过程 本文主要从进程栈空间的层面复习一下C语言中函数调用的具体过程,以加深对一些基础知识的理解.     先看一个最简单的程序: 点击(此处)折叠或打开 /*test.c*/ ...

  4. C语言变量定义和赋值

    定义变量的格式非常简单,如下所示: 数据类型  变量名; 首先要强调的一点是:最后的分号千万不要丢了.变量的定义是一个语句,我们说过,语句都是以分号结尾的. "数据类型"表示想要存 ...

  5. c语言 宏do while,关于C语言宏定义 使用do{ xxxx }while()

    暂时感觉像是由于":"的原因,关于使用习惯方面的问题!! 下面是copy的: 这样的宏见过么: Cpp代码 #define FOO(x) do {\ some_code_line_ ...

  6. c语言字节溢出,C语言变量定义与数据溢出(初学者)

    1.变量定义的一般形式为:类型说明符.变量名标识符等:例:int a,b,c;(abc为整型变量) 在书写变量定义时应注意以下几点: (1)允许在一个类型说明符后,定义多个相同类型的变量.各变量之间用 ...

  7. c语言中定义结构体指针的作用,C语言结构体定义,使用及指针(1)

    前言 C语言中的结构体作为一大难点,并且对于需要学习数据结构的同学来说,这个结构是每次上课都会遇到的,所以,我们只有更深层次的理解结构体的知识,才能更好的掌握数据结构 结构体用处 结构体到底有什么用? ...

  8. c语言cyc函数,深入理解C语言

    导读:Dennis Ritchie过世了,他发明了C语言,一个影响深远并彻底改变世界的计算机语言.一门经历40多年的到今天还长盛不训的语言,今天很多语言都受到C的影 响,C++,Java,C#,Per ...

  9. 理解C语言(零) 导读(下):有用的C语言工具-从Make说起

    理解C语言(零) 导读(下):有用的C语言工具-从Make说起 1 Make 在GNU中提供了一个用于管理多个C源代码文件的项目管理工具,用户只需按照一定的语法规则编写这个Makefile文件.输入m ...

最新文章

  1. “应付”大学作业,我花3小时写了一个“文本转手写”神器
  2. Android Activity的launchMode四种启动模式备忘
  3. 微信8.0全心全意考虑用户需求为企业开展网络营销带来全新思考
  4. python3.5.0下载-Python for Linux
  5. 多核服务器的JVM优化选项(转载)
  6. HDU - 2204 Eddy‘s爱好(尚未完全解决)
  7. 【Java】字符串转换为数字:Integer的parseInt方法
  8. Difference between stem and lemma
  9. 百度推出开发者搜索 Beta;雷军手机使用时长曝光;苹果败诉,电脑上可以模拟 iOS 系统 | 极客头条...
  10. 程序员的自我修养—链接、装载与库pdf
  11. 数值分析(11):常微分方程的数值解法之Euler法
  12. 阿酷三合一版_阿酷插件下载|3dmax阿酷插件下载 v3.2 开源版 - 比克尔下载
  13. 高效工作的浏览器插件
  14. 淘口令 java,抓包获取淘口令的解决方案
  15. 三极管参数应用大全(转载)
  16. Linux - 一次性计划任务之at命令使用
  17. 超好用的PC截图工具Snipaste—免费便捷高效
  18. mapboxGL中多图标加载的实现
  19. 轨道运营管理专业自荐书_城市轨道交通运营管理专业自荐信范文【精选】
  20. 自定义Echarts图表图例的图片

热门文章

  1. 2022: BLIP: Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and
  2. ORACLE中,对于存储过程中获取到dd-mm -yy格式的数据,如何做转换。
  3. animations 动画图片的播放
  4. C#中HttpWebRequest的GetRequestStream执行的效率太低,甚至偶尔死掉
  5. CSS|奔跑的熊大 案例(有图+代码)
  6. 机架服务器扩展硬盘柜,强悍扩展性三款多硬盘盘位机架式NAS导购
  7. 显示器属于计算机网络连接设备吗,下列属于计算机外部设备的是()。A.调制解调器B.音箱C.硬盘D.扫描仪E.显示器...
  8. 飞凌FCU1104嵌入式控制单元JAVA读取串口
  9. 视频号视频发布软件助手功能都有哪些?是什么软件?
  10. Radar And Millimeter-Wave System Based On Photonic Technology