前言:
现时朋友们对“c语言中结构体的定义”可能比较关注,姐妹们都想要分析一些“c语言中结构体的定义”的相关知识。那么小编在网摘上网罗了一些有关“c语言中结构体的定义””的相关文章,希望你们能喜欢,姐妹们快快来了解一下吧!本节,我们着重研究结构体的定义,也就是struct这种变量定义,c语言编译器是如何解析的,本节我们要解析的结构体定义如下:
struct tag{ int x; int y; char z; struct tag *p;}name;
结构体定义的解析语法
type_specifier->struct_specifierstruct_specifier->struct opt_tag LC def_list RC | struct tagopt_tag->tagtag->NAMEdef_list->defdef_list->def_list defdef->specifiers decl_list SEMI | specifiers SEMIdecl_list->decl | decl_list COMMA decldecl->var_declvar_decl->new_name | var_decl LP RP | var_decl lp var_list rp | lp var_decl rp | star var_decl
我们先看这一句定义:
struct_specifier -> STRUCT opt_tag LC def_list RC
STRUCT就是关键字struct对应的token,opt_tag对应的结构体变量的名字tag,LC对应左大括号,def_list对应的结构体内部变量定义序列,RC就是右大括号,这一句语法就已经描述了整个结构体的定义,解析的重点其实是在def_list,这个非终结符描述的是结构体内部变量的定义规则,对def_list的解析是整个解析过程的难点。
语法解析流程描述
解析开始时,词法计息器会把关键字struct读入,并返回一个STRUCT标签
读入struct后面变量名tag,返回对应的标签NAME
根据表达式tag->NAME,将NAME转化为非终结符TAG
通过表达式opt_tag->tag,将tag转换成非终结符opt_tag
读入左大括号,并返回LC
读入关键字int 返回对应的token TYPE
根据表达式type_specifier->TYPE 将TYPE替换成type_specifier,这个表达式在前面的张杰中将结果,本节没有列出来
根据表达式type_or_class->type_specifier将非终结符type_specifier转换成type_or_class
根据表达式specifier->type_or_class,将非终结符type_or_class转换成specifier
上面几步完成了对关键字int的解析
接着读入变量名想,返回对应的标签NAME
根据表达式new_name->NAME,将非终结符new_name压入堆栈
根据表达式var_decl->new_name,将非终结符转换成var_decl
通过表达式decl->var_decl,将非终结符var_decl替换成decl
通过表达式decl_list->decl,将非终结符decl_list压入堆栈
读入分号,并返回对应的标签SEMI
根据表达式def->specifiers decl_list SEMI,将解析丢站上的符号全部弹出,转换成非终结符def,
再次通过def_list->def将非终结符def_list压入堆栈
在这一步我们可以看到int x;这个变量的定义语句可以被非终结符def_list所描述
通过表达式 DEF_LIST -> DEF_LIST DEF 将两个非终结符归纳成一个非终结符DEF_LIST.
解析器继续读入 “char z;”, 同样经历带*号的解析步骤后,把该语句解析成非终结符DEF, 这时解析堆栈头部又在此含有两个非终结符 DEF_LIST, DEF, 于是又可以通过表达式 DEF_LIST -> DEF_LIST DEF 将两个非终结符归纳为一个非终结符DEF_LIST.
接下来解析器再次遇到关键字struct, 读入后返回对应标签STRUCT.
入读struct 后面的变量名tag,返回对应标签NAME.
入读struct 后面的变量名tag,返回对应标签NAME.
运用表达式 TAG -> NAME, 将非终结符TAG压入堆栈。
采用表达式STRUCT_SPECIFIER -> STRUCT TAG 将堆栈顶部的两个非终结符替换成STRUCT_SPECIFIER.
再通过TYPE_SPECIFIER -> STRUCT_SPECIFIER 将栈顶非终结符替换成TYPE_SPECIFIER.
接着分别通过两个表达式TYPE_OR_CLASS -> TYPE_SPECIFIER 和 SPECIFIERS -> TYPE_OR_CLASS, 将栈顶元素替换成SPECIFIERS.
然后把代表指针的* 读入,返回对应标签STAR.
读入星号后面的变量名p, 返回对应的标签NAME.
通过表达式NEW_NAME -> NAME, 将非终结符NEW_NAME压入解析堆栈。
通过VAR_DECL -> NEW_NAME 将栈顶元素替换成VAR_DECL.
此时栈顶元素包含VAR_DECL, 和 STAR, 这两个元素正好形成表达式:
VAR_DECL -> STAR VAR_DECL 的右边部分,于是经过一次reduce,将堆栈顶部的两个元素替换成VAR_DECL.
继续通过表达式DECL -> VAR_DECL, 将非终结符DECL压入堆栈。
DECL 可以通过表达式DECL_LIST -> DECL 替换成DECL_LIST.
接着读入变量p后面的分号,返回对应标签SEMI
此时,解析堆栈上含有三个元素:SEMI, DECL_LIST, SPECIFIERS, 他们正好构成表达式DEF -> SPECIFIERS DECL_LIST SEMI 的右边,于是通过该表达式进行一次reduce, 将DEF替换掉这三个符号。
此时,堆栈顶部有两个元素,DEF, DEF_LIST, 又正好构成表达式:
DEF_LIST -> DEF_LIST DEF 的右边,于是又可以用DEF_LIST 替换掉堆栈顶部的两个元素。
接着读入右括号 }, 返回对应标签RC.
这时,堆栈顶部的5个元素正好对应表达式:
STRUCT_SPECIFIER -> STRUCT OPT_TAG LC DEF_LIST RC 的右边,于是解析器可以一下子将这5个元素全部替换成STRUCT_SPECIFIER.
接着解析器可以通过TYPE_SPECIFIER -> STRUCT_SPECIFIER 将TYPE_SPECIFIER压入堆栈。
然后把}后面的变量名name,读入,解析流程跟前面讲解的流程一样。
读入最后的分号后,解析堆栈上的元素正好构成表达式:
EXT_DEF -> .OPT_SPECIFIERS EXT_DECL_LIST SEMI
的右边部分,于是整个解析堆栈顶部的元素全部弹出,换成符号EXT_DEF.
接下来的推导跟以前一样,经过一系列固定步骤后,全局非终结符PROGRAM会被压入堆栈,从而使得解析器接收输入文本。
由此可见,依赖本节给出的语法定义,解析器能够顺利地分解结构体的代码。
通过这几节的解析流程分析,我们可以看到,写得再繁杂,再杂乱无章的程序代码,只要符合语法,那么这些看似随机组合的字符或单词,本质上遵从着一种非常严谨的层次和结构,这种层次和结构可以通过语法定义的方式描述出来,大道至简,任何复杂的系统,其本质都可以归因为若干简单的原理。这就是科学之美,编译原理的算法之美,学习编译原理或任何科学知识,其实是一种享受美的过程。
要体验这种系统逻辑之美,需要巨大的耐心,和不厌其烦地探索,持之以恒的意志力,有这种恒心的人,才有可能“会当凌绝顶,一览众山小”,学习是一个不断攀爬,跌倒,再攀爬的过程,只有咬紧牙,永不放弃的人,才有可能在光明顶感受到“荡胸生层云,决眦入归鸟”的人生成就感。
再次以王安石《游褒禅山记》为每一位愿意“吾将上下而求索”的同学共勉:
“古人之观于天地、山川、草木、虫鱼、鸟兽,往往有得,以其求思之深而无不在也。夫夷以近,则游者众;险以远,则至者少。而世之奇伟、瑰怪,非常之观,常在于险远,而人之所罕至焉,故非有志者不能至也。有志矣,不随以止也,然力不足者,亦不能至也。有志与力,而又不随以怠,至于幽暗昏惑而无物以相之,亦不能至也。然力足以至焉,于人为可讥,而在己为有悔;尽吾志也而不能至者,可以无悔矣,其孰能讥之乎?此余之所得也!”
相信我,你,并不孤独!
标签: #c语言中结构体的定义