我们本篇博客的内容主要是解决如何阅读C语言的声明。比如:
以上的声明形式我想大部分我们都会遇到(实际上我们上面的形式都会用到),针对这些形式,我们会给出解析的方法。
变量的声明形式主要有两种解析的方法,一种是定义解析法,一种是形式解析法。
定义解析法就是依据变量声明的定义方法去解析,比如说int a;我们根据int类型的变量定义形式,我们很容易知道a是一个int类型的变量。这种解析法,简单直观,但是对于复杂声明的解析就比较困难,因为我们不是很容易给出一个简单的定义形式来表示复杂的声明。
形式解析法是依据从右向左解析的方式,每次解析只针对特定的部分。这种解析方式需要在原有的形式上做些形式转化。我门使用符号“{}”来定义解析元素的概念,下面重点介绍这个方法(此方法是我个人的理解,但是,的确很有用,这个方法只是形式上的变化)。
形式: char a;
如果要做形式转换首先我们要确定那个东西是变量。变量的基本定义中有一条是变量不可能是关键字的字符串,依据这点我们很容易转换上述的形式:
char a;—> {char}2 {a}1
解析的方式依据从右向左的方式,
1.{a}1表示这是一个变量,变量名是a。
2.{char}2表示这个变量是一个char类型的变量。
依据上面的解析说明,我们知道这个声明表示我们定义了一个char类型的变量a。
形式:char * b;
转换后的形式:{char}3 {*}2 {b}1
解析步骤:
1.{b}1 表示这是变量名为b的变量
2.{*}2 表示这个变量是一个指针类型的变量
3.{char}3 表示这个指针类型的是一个char类型
总结:定义了一个char类型的指针变量b
形式:const char * c;
转换后的形式:{char}4 {const}3 {*}2 {c}1
解析步骤:
1. {c}1 表示这是一个变量名为c的变量
2. {*}2 表示这个变量是一个指针类型的变量
3. {const}3 表示这个指针类型变量不能作为左值
4. {char}4 表示这个指针类型是一个char类型
总结:定义了一个char类型的指针变量c,该变量的间接引用形式的表达式不能作为左值
说明:
1. const char 和形式char const 是具有同样的定义
2. const的语义只是是限定定义的变量空间不可被修改,这种语义用另一种方式来说就是,这个变量空间不能做为左值的。上面的形式有个额外的意思指针类型的表达式不能做为左值的, 也就是说*(c+1) = ‘x’;这个表达式不允许的。
形式:char * const d;
转换后的形式: {char}4 {*}3 {const}2 {d}1
解析步骤:
1. {d}1 表示这是一个变量名为d的变量
2. {const}2 表示这个变量是不能作为左值
3. {*}3 表示这个变量是一个指针类型的变量
4. {char}4 表示这个指针类型是 char 类型
总结:定义了一个char类型的指针变量d, 该变量不能作为左值
形式: char e[100];
转换后的形式:{char}3 {[100]}2 {e}1
解析步骤:
1. {e}1 表示这是一个变量名为e的变量
2. {[100]}2 表示该变量是一个数组类型的变量,这个数组大小是100
3. {char}3 表示这个数组存储的值是char类型
总结: 定义了存储char类型的值大小为100的数组变量e
说明:数组的解析的方式之所以困难主要是他的转换形式和我们定义形式是不同的,之所以这样的转换,是因为这种形式具有一般性,后面的解析形式会有更进一步的说明
形式: char *f[100];
转换后的形式: {char}4 {*}3 {[100]}2 {f}1
解析步骤:
1. {f}1 表示这是一个变量名为f的变量
2. {[100]}2 表示这个变量是一个数组类型的变量,数组的大小是100
3. {*}3 表示这个数组存的是指针类型的值
4. {char}4 表示这个指针类型是一个char类型
总结:定义了一个存储char指针类型的值大小为100的数组变量f
说明:
1. 运算符’[]’ 结合的优先级比’*’高
形式:char (*g)[100];
转换后的形式:{char}4 {[100]}3 {*}2 {g}1
解析步骤:
1. {g}1 表示定义了一个变量名为g的变量
2. {*}2 表示这个变量是一个指针类型的变量
3. {[100]}3 表示该指针类型是一个大小为100的数组类型
4. {char}4 表示该数组存储char类型的值
总结:定义一个存储char类型值的大小为100的数组类型的指针变量g
说明
1. 如果有’()’这里面的表达式需要优先解析
形式:struct student h;
转换后的形式:{struct}3 {student}2 {h}1
解析步骤:
1. {h}1 表示定义了一个变量名为h的变量
2. {student}2 表示定义了自定义类型student的变量
3. {struct}3 表示这个自定义的类型是一个结构体类型
总结:定义了一个结构体为student类型的变量h.
说明 :
1. student是一个自定义的类型,也就说我们必须优先定义好这个类型。C语言的变量类型,主要分成两大部分,一部分是内建的,另一部分是自定义的,函数类型也是一种自定义的类型。
形式:int def1();
转换后的形式:{int}3 {()}2 {def1}1
解析步骤:
1. {def1}1 表示定义了一个变量名为def1的变量
2. {()}2 表示该变量是一个函数类型的变量
3. {int}3 表示该函数的返回类型是一个int类型的值
总结:定义了一个返回值为int类型的函数变量def1
形式:int * def2();
转换后的形式:{int}4 {*}3 {()}2 {def1}1
解析步骤:
1. {def2}1 表示定义了一个变量名为def2的变量
2. {()}2 表示该变量是一个函数类型的变量
3. {*}3 表示该函数的返回值类型是一个指针类型的值
3. {int}4 表示该指针类型的是一个int类型
总结:定义了一个返回值为int类型的指针类型的值的函数变量def2
形式:int (*def3)();
转换后的形式:{int}4 {()}3 {*}2 {def3}1
解析步骤:
1. {def3}1 表示定义了一个变量名为def3的变量
2. {*}2 表示该变量是一个指针类型的变量
3. {()}3 表示该指针类型是一个函数类型
3. {int}4 表示该函数返回值是一个int类型
总结:定义了一个返回值为int类型的函数类型的指针变量def2
如果善于总结的读者,可能会从我上面的分析过程中的得到一些规律的东西,个人很惭愧无法说明这种规律性,但是,我感觉这种规律应该是定义C语言的语义的时候就有的,也就说,可以编译原理的语法解析层面来进行解释。
作为一个练习,我们来看一个形式(这个形式是真实存在在标准库中的):
void (*signal(int sig, void(* func)(int))) (int);
这个形式看上去很复杂,因为这含有一个嵌套的类型定义,但是他的转换形式很简单:
转换后的形式:{void}5 {(int)}4 {*}3 {(int sig, void (*func)(int))}2 {signal}1
解析步骤:
1. {signal}1 定义了变量名为signal的变量
2. {(int sig, void (*func)(int))}2 表示该变量是一个函数变量
3. {*}3 表示该函数的返回一个指针类型的值
4. {(int)}4 表示该指针类型是一个函数类型
5. {void}5 表示该函数返回无类型的值
总结:定一个返回无类型的函数的指针类型的函数变量signal。
非常难以说明!!!!