C语言中申明解析方法的总结

转载自:http://soft.chinabyte.com/database/248/12193748.shtml

《C专家编程》第3章学习

  C语言中的声明是一个比较令人头痛的问题,尤其一些复杂的声明看起来甚至会令人感到恐怖,如果您是一位初学者,您一定会对下面这几个式子感到畏惧。因为对于刚接触这种形式的人来说,这简直太复杂了,当然前提是假定您未掌握分析这方面的专业知识。

  第一个声明: void(*signal(int sig, void (*func)(int)))(int);第二个声明:const char *const(*sinc(char *sincg(),int (*p)(int a,int *b)))(int **sing);其中第一个声明是某系统函数的声明,而第二个声明是我自己即兴创作的,但我敢保证它除了复杂一点之外没任何缺点。如果你对这两个声明感到异常头痛,并且急迫的想知道如何分析这样的声明来提高自己的c水平和熟练度。那么就请阅读下面的内容,让我将自己所学到的知识与您一起分享吧!

  首先我们来看一个很简单的例子——复杂的东西都是由简单的构成,一旦将所有简单的都搞明白了,你才能,并且也可以轻易的弄懂复杂的问题,而那时候便只需要某一个转机就行了,那转机往往就是总结。

  char next ;大家都很容易知道,这里声明了一个char 类型的变量next.这里的next是一个标识符,表明它的变量身份,所以我们可以这样想,一旦碰到标识符,如同next,就用这样一句话替代,(标识符)是………,比如前面的next,可以用next是……来替代,如同c语言中的宏定义一样,如果这样的说法让你感到有点晕,我们换一种方式,干脆这样定义一下,#define 标识符标识符是……

  也就是说,一旦碰到标识符,你脑海中就立刻用(标识符)是………这样一句话来完全替代那个标识符。于是,刚才的那个声明,char next;我们用更规范的类似数学上的形式思维来考虑一下,首先我们分析标识符next,根据前面的说法,我们看到next之后,立刻用next是…。来替代,这样,我们就得到了整个声明的前一部分:“next是……。”这样一个句子,但是next究竟是什么呢,这时候我们需要看声明中的其他内容,比如上例中的char,这时候我们很自然的就弄清楚了next是(一个char类型的变量)。所以char表明的是一个类型变量。于是,经过这样一个思维过程,上面的声明就分析完成了。

  大家先不要失去耐心,我用这么长的篇幅来叙述这样一个简单的例子并不是掉你胃口,相反,是为了让你更加容易理解下面的内容。使我的文字跳跃度不至于让你的思维感到突兀甚至短路——这不是我写这篇总结的目的。

  接下来我们把这个声明稍微加长一点,使它变成char next();或者是char next[];这时候,根据我们所学的知识,依然能够很轻易的看出,前面一个是声明的返回char类型变量的函数,而后一个是声明一个char类 型变量的数组,正是这个时候,有一个问题请你注意,我们添加这个括号之后究竟使我们的思维改变了什么,为什么当这两个符号出现之后我们里就就明白了这是一 个函数或是一个数组,所以,在这里我想告诉你也许你从未意识到过的一个新知识点就是,圆括号和方括号是声明中的最高优先级[/B].这是一个非常重要的知 识点,当你发现标识符的右边紧挨着一个左圆括号(请一定注意到我这里写的是左圆括号[/B])或者是方括号的时候,你就不要有任何怀疑的告诉自己,标识符 是一个(返回…的函数)或者数组。让我们用这样的思维再来分析一下char next();这个声明,首先分析标识符next,得出next是…。,然后向右看它右边紧靠它的是不是圆括号或者方括号,这个例子中是一个圆括号,所以我们得出next是一个返回…。的函数,最后通过char类型符得出,next是一个返回char型变量的函数,至此,整个声明分析完成。

  好了,现在让我们来总结声明分析中的前两个基本步骤,也是关键步骤。

  首先,分析声明中的标识符,这里有一个问题,不知道大家发现没,我上面举的例子中都是只有一个标识符,如果声明中出现多个标识符怎么处理,比如说在next函数中增加几个参数,如char next(int a,int b);这 样的话,整个声明中有三个标识符,我们究竟首先选取分析哪个标识符呢。通过这个简单的例子,我们很容易观察出,首先选择的是最左边的标识符,当然,我们决 不能根据一个特例就得出普遍结论,在这里,我以一个已经掌握了这个知识点的人的身份告诉你,你的猜想是正确的,标识符从最左边的开始处理,c中确实是这样做的。那么现在,你也掌握了这个知识点,让我们继续向下讨论。当我们选定标识符后,就要观察紧靠它右边的声明器(也就是在声明中出现的各种各样的符号和变量,如(),*,const,[]等的官方说法),这里分两种情况:如果出现的是一个左圆括号,那么标识符就是一个返回…。的函数,如果出现的是一个方括号,则毫无疑问的说明,标识符是一个数组,至于是一个什么样的数组,则必须通过之后的分析才能够知道。

  好了,现在又出现了一个新的问题,那就是如果标识符的紧邻的右边既不是左圆括号,也不是方括号怎么办?比如是一个右圆括号呢。这就需要我们的第三个比较重要的分析声明的步骤。那就是:看标识符左边的符号情况,分以下两种:A, 如果紧邻标识符左边的是一个左圆括号,则找寻到和它匹配的右圆括号,将整个括号内的声明当成一个整体分析。

  B, 如果紧邻标识符左边的是*,或者const,或者volatile三者之一,则继续向左查找,直到声明器超出三者之外为止。也就是说要一直找到某个符号既不是*,const,也不是volatile为止。

  C, 继B之后,如果符号是左圆括号,则回到A进行处理。

  最后,剩下的符号可以一并阅读,因为那一定已经是非常容易理解的了。

  在这里,我想为初学朋友解释一下const和volatile两个修饰符,const表示所修饰的变量是只读的,也就是一经赋值就不能再被修改。例如const char p; const char *p;此处需要大家注意的就是,const char *p;和char * const p;是不同的声明,前一个表明指针p所指向的内容是只读的,而后一个则表示指针本身是只读的,而它指向的内容则是可以改变的。我总结了一个规律供大家记忆方便,那就是如果 *和标识符是一个整体,没有被任何东西分开,则说明const修饰的是指针指向的内容,如上例中的const char *p;或者是char const *p;*和标识符(p)没有被const这个修饰符分开,则说明const修饰的是指针指向的内容,而一旦*和标识符被分开了,则说明const修饰的是指针本身,而指针指向的变量则是可变的。除非指向的变量本身也被const修饰了,如const char * const p;第二个修饰符volatile表明修饰的变量是他是可以被本程序和别的程序改变数据,象系统时间,不管这个程序是不是断点,sleep,别的程序都改变他的值。

  好了,如果大家仔细阅读了上面的部分,一定会发现其实细心去分析之后,声明的理解也并不如何困难,现在我们先来用一个比较常规的声明来熟练一下上面的思维方式,最后我们以解决本文开始提到的两个声明作为结尾。

  char (*p)(); //首先看最左边的标识符p,(表明p是……)他的右边既不是左圆括号也不是方括号,于是看左边,发现是*,根据上面的原则(如果紧邻标识符左边的是*,或者const,或者volatile三者之一,则继续向左查找,直到声明器超出三者之外为止。也就是说要一直找到某个符号既不是*,const,也不是volatile为止。)我们继续向左查找,发现下一个符号是(, 于是根据原则(继B之后,如果符号是左圆括号,则回到A进行处理。)我们找寻匹配这个左圆括号的右圆括号,那么,我们就可以把这个括号里的东西作为一个整体来处理,该例子中括号内的内容为*p,所以我们得出p是 一个指针,既然是一个指针,就肯定指向某一样东西,而这个东西,我们必须继续探究才能把它给挖出来,现在我们已经把括号内的东西当成一个整理处理了,就相 当它是一个变量(我们通过分析这个括号内的东西确实得出了一个指针类型的变量,)于是我们可以把它当成最初的标识符,回到第一步分析,看这个变量(实际上 是把整个括号看成一个标识符,分析这个括号最靠近右边的符号)左边临近的是否是左圆括号或者是方括号,很幸运的,我们发现了左圆括号,就说明这个变量(也 就是整个括号)是一个返回…的函数,而原来那个等价于变量的括号内部事实上是一个指针变量,再结合我们刚才分析出的,就可以知道,这个指针变量指向一个返回…。的函数,最后我们分析这个函数究竟返回什么,这时候只剩下一个char类型符了,所以整个声明的内容是,一个指向函数的指针,该函数返回一个char型的变量分析出这样一个声明并不困难,难就难在用上面的思维进行分析,上面的分析步骤事实上是编译器进行声明分析的步骤,但其实就上面总结的还并不完全。那么下面我就将完整的声明的分析步骤罗列出来:分析的步骤匹配的符号阅读方式1 取最左边的标识符标识符表示标识符是……

  2查看标识符右边下一个 [可能的大小对于每一对,表示符号,如果是方括号 ……的数组3 如果是一个左圆括号 ( 可能的参数) 到右括号为止的内容表示返回……的函数4 如果左边的符号是一个 ( 这个左括号把已经处理的左圆括号部分声明组合在一起,直到遇见对应的右括号,然后从第二步 重新开始5 如果左边的符号是下述符号之一*,const,volatile *,const,volatile 继续向左读符号,知道声明器 超出 三者之外,然后重复第4步6 剩下的符号形成基本类型基本类型如char,int 剩余的符号可一并阅读

  以上就是声明分析的完整总结版,也是c专家编程书中罗列出来的,我这里只是借鉴一下,供大家学习参考。

  好了,有了以上的基础,我们就好分析第一个恐怖型的声明,从这样的声明中,我们上面的分析步骤的优势就体现出来了

  void(*signal(int sig, void (*func)(int)))(int);

  首先,最左边的标识符是signal,表明signal是…。,紧靠它右边的是一个左圆括号,说明signal是一个返回…的函数,而int sig, void (*func)(int)是这个函数的参数,里面的每一个部分都可以重新用我们的方法进行分析,这里就不叙述了。然后我们根据上面的分析步骤,再看左边的符号是什么,是一个指针符*,表明该函数是一个返回一个指针的函数,此时,函数已经被我们简化成void p(int);j其中p是一个返回指针的函数,是我们上面分析所得出的结果。那么该指针指向的是什么呢,这里再根据前面的步骤,分析整个括号的临近右边的符号,也就是p(我们已经将整个括号中的内容等价为p这个假想中的标识符)右边的符号,发现是一个左圆括号,所以p是一个返回…的函数,而p是一个指针(实际上是某一个函数所返回的指针),所以p是一个指向函数的指针,最后根据void判断出,该函数的返回值为空。这样,一个复杂的声明就被我们抽丝剥茧的分析完了、用完整版的语言来叙述这个声明:这是一个函数,该函数有两个参数(参数的声明分析大家自己完成哈),并且该函数返回一个指针,该指针指向一个函数,该函数有一个参数,并且返回类型为空。

  好了,写到这里,希望阅读本文的您已经理解了我所想表达的内容,并且处于对您的尊重,我就将第二个看来更有挑战性的声明交给您来完成。如果有什么问题或者发现本文有什么观点错误的地方,非常欢迎来群里讨论,也十分愿意同您交一个好朋友。

  让我们一起学习进步。

时间: 2024-10-20 13:23:54

C语言中申明解析方法的总结的相关文章

C语言中常用计时方法总结

转自:http://blog.csdn.net/fz_ywj/article/details/8109368 C语言中常用计时方法总结 1. time() 头文件:time.h 函数原型:time_t time(time_t * timer) 功能:返回以格林尼治时间(GMT)为标准,从1970年1月1日00:00:00到现在的此时此刻所经过的秒数. 用time()函数结合其他函数(如:localtime.gmtime.asctime.ctime)可以获得当前系统时间或是标准时间. 用difft

xml解析----java中4中xml解析方法(转载)

转载:https://www.cnblogs.com/longqingyang/p/5577937.html 描述 XML是一种通用的数据交换格式,它的平台无关性.语言无关性.系统无关性.给数据集成与交互带来了极大的方便.XML在不同的语言环境中解析方式都是一样的,只不过实现的语法不同而已. XML的解析方式分为四种:1.DOM解析:2.SAX解析:3.JDOM解析:4.DOM4J解析.其中前两种属于基础方法,是官方提供的平台无关的解析方式:后两种属于扩展方法,它们是在基础的方法上扩展出来的,只

MVC,如何在视图中申明方法,调用方法?

<div> <!--在视图中申明方法,此方法的类型已经固定为HelperResult--> @helper ShowHello(string s) { <div> 哇塞!!!@s </div> } </div> <div> <!--调用在视图中申明的方法--> @ShowHello("cfs") </div> 效果图: <div> <!--在视图中申明方法,此方法的类型已经

(转)使用 CJSON 在C语言中进行 JSON 的创建和解析的实例讲解

使用 CJSON 在C语言中进行 JSON 的创建和解析的实例讲解 本文用代码简单介绍cjson的使用方法,1)创建json,从json中获取数据.2)创建json数组和解析json数组 1. 创建json,从json中获取数据 1 #include <stdio.h> 2 #include "cJSON.h" 3 4 char * makeJson() 5 { 6 cJSON * pJsonRoot = NULL; 7 8 pJsonRoot = cJSON_Create

使用C语言为python编写动态模块(2)--解析python中的对象如何在C语言中传递并返回

楔子 编写扩展模块,需要有python源码层面的知识,我们之前介绍了python中的对象.但是对于编写扩展模块来讲还远远不够,因为里面还需要有python中模块的知识,比如:如何创建一个模块.如何初始化python环境等等.因此我们还需要了解一些前奏的知识,如果你的python基础比较好的话,那么我相信你一定能看懂,当然我们一开始只是介绍一个大概,至于细节方面我们会在真正编写扩展模块的时候会说. 关于使用C为python编写扩展模块,我前面还有一篇博客,强烈建议先去看那篇博客,对你了解Pytho

IOS中Json解析的四种方法

作为一种轻量级的数据交换格式,json正在逐步取代xml,成为网络数据的通用格式. 有的json代码格式比较混乱,可以使用此“http://www.bejson.com/”网站来进行JSON格式化校验(点击打开链接).此网站不仅可以检测Json代码中的错误,而且可以以视图形式显示json中的数据内容,很是方便. 从IOS5开始,APPLE提供了对json的原生支持(NSJSONSerialization),但是为了兼容以前的ios版本,可以使用第三方库来解析Json. 本文将介绍TouchJso

C语言中宏定义使用方法详解

C语言中的宏替换详解 首先看一个问题: #include <stdio.h> #define    PRINT_CLINE()    printf("%d", ______) int main(void) { PRINT_CLINE(); PRINT_CLINE(); return 0; } 在横线处填上适当的代码,使得上面这段代码的输出为34. 我想一般人看到这个问题的时候头脑里都没有明确的思路来解答这个它.我看到这个问题的时候想出了各种办法来解答它,最终还是没有通过编译

java中构造方法和方法全面解析

java中构造方法和方法全面解析 我相信大多说人都对构造方法.方法不陌生,而且很了解,但我也相信有很多像我这样的没有一个很好很清晰很全面的认识,今天就把它整理如下,希望能给大家带来点方便与帮助,也希望大家多多讨论.          构造方法和方法的区别: 构造方法要与类名相同,无返回类型,在类初始化的时候调用.      方法最好与类名不同,对象调用,静态方法可用类名.方法(). 构造器和方法在下面三个方面区别:修饰符,返回值,命名. 1.和方法一样,构造器可以有任何访问的修饰: public

Java不规则字符串按照逗号分割的解析方法(字段中又含有逗号)

/** * Java字符串逗号分割解析方法 * 本专门针对双引号中还有逗号或者某个字段无双引号的情况而设计的 * 例如要将 字符串 String sss="101,\"a\",\"中国,江苏\",\"b\",\"中国,北京\",1,0,\"c\""按照逗号进行分割解析; * 正确的split的结果是 (101)(a)( 中国,江苏) ( b)( 中国,北京) ( 1)( 0)( c) *