相容类型Compatible Type

在前文对函数调用进行语义检查时,我们用函数CanAssign()来判断“能否能把实参赋值给形参”,在函数CanAssign中,我们又用宏IsCompatiblePtr来判断两个指针是否相容。而如果指针变量T1 * ptr1和T2 * pt2相容,则意味着类型T1和T2是相容的。UCC编译器中ucl\type.c的函数IsCompatibleType()用于判断类型是否相容,与类型系统相关的数据结构请参见”2.4节 C语言的类型系统”中的各个示意图,而在这一节中,我们要对与IsCompatibleType()相关的几个函数做进一步讨论。我们先举个简单的例子来说明类型相容的概念,以下两个对arr的声明被视为兼容的,其出发点是遇到intarr[]时,我们只是暂时不知道其数组大小,稍后遇到了intarr[3]时,我们知道其数组大小为3,因此以下对arr的两次声明对应的是内存中的同一个数组对象。需要注意的是,以下对于a的两次声明被视为是冲突的,因为int占4字节,double占8字节,两者内部的数据组织形式也不一样,如果硬要规定把a当double来处理,那程序员在分析以下函数f()时,若没有注意到离得比较远的double a,却发现在f()函数中sizeof(a)竟然是8,那其脑门上不知要浮现出多少个问号。但需要注意的是,进行赋值时,我们可以把整型的b赋值给double型的c,这需要编译器进行隐式的类型转换,但不论如何转型,程序员使用变量名b和c时,C编译器与C程序员的约定仍旧是“b对应内存中的一个int类型的变量,而c对应double类型的变量”。虽然在UCC编译器exprchk.c的个别地方用到了IsCompatible()函数,但这个函数主要还是用在declchk.c中,换言之,我们主要是在处理形如int arr[]和int arr[3]的声明时,用IsCompatible()函数来检查一下同名的两个声明其类型是否相容,后续在分析declchk.c时我们会看到这一点。

int arr[]; // compatible

int arr[3];

int a; // incompatible

void f(){

}

double a;

int b; double c; //

c = b;

接下来,我们来分析一下IsCompatible()函数的代码,如图4.2.31所示。图4.2.31第3行用于判断ty1和ty2所指向的是否为同一个struct type对象,如果是,则肯定相同类型的对象,自然就是彼此兼容的;否则还需要进一步判断。

图4.2.31 IsCompatible()

在UCC编译器中,我们可用图4.2.31第3行的条件来判断整型和浮点型,例如对int a和int b而言,由于UCC编译器用同一个struct type对象来表示在源代码出现的所有int类型,所以a和b的类型信息都存放在T(INT)所指向的struct type对象中;但对int c和char d而言,c和d分别对应T(INT)和T(CHAR),它们指向两个不同的struct type对象,因此,图4.2.31第3行的条件不成立,即int 和char不是相容的类型。再次强调的是,我们可以进行int型的a和char型的d之间的相互赋值,但是由于int 和char是不相容的,所以在同一个作用域中,“同时声明charc和int c”则是错误的。UCC编译器用以下数组Types来表示整型和浮点型对应的类型对象struct type。

#define T(categ) (Types + categ)

struct type Types[VOID – CHAR + 1];

而const int和int也被视为不相容的类型,因为两者在限定符(const或者volatile)上不一致,图4.2.31第5行的条件用于此目的。而int *和struct Data显然风马牛不相及,一个是指针类别,一个是结构体类别,第13行的if条件用于对此进行判断。对于形如T1 * 和T2 *的类型来说,两者都是指针类别,我们就在第17行递归地判断T1和T2是否兼容。而对于数组类型T1 [m]和T2[n]来说,两者相容的条件是“T1和T2相容”并且“m和n大小一样,或者两者中有一个为0”,例如int [3]和int [0]是相容的,图4.2.31第18至20行完成这样的判别。对函数类型来说,我们还需要递归地检查一下两个函数的对应参数和返回值类型的相容性,这通过在第22行调用IsCompatibleFunction()函数来处理。而对于整型等基本类型、结构体和联合体来说,我们通过第24行的条件来检查ty1和ty2是否指向同一个struct type对象。例如,UCC编译器会为以下结构体struct Data1和struct Data2各建立一个struct type对象,用来存放它们的类型信息,这意味着,即使这两个结构体的所有成员的名称和类型都一样,但struct Data1和struct Data2是不相容的。

struct Data1{

int a;

double b;

};

struct Data2{

int a;

double b;

};

对于枚举类型和int来说,如果要把这两者视为兼容的,则删去图4.2.31第9至12行的注释号//即可。把这两种视为相容类型的理由可以是,枚举类型实际上就是范围受限的整型。按ucc\ ansi.c.txt第” 3.5.2.2Enumeration specifiers”节的说明,这两者是相容的(Eachenumerated type shall be compatible with an integer type)。但是GCC和Clang视之为不相容类型,会对以下声明进行报错。保留上图4.2.31第9至12行的注释号,则UCC编译器也可以对以下声明进行报错“error:Incompatible with previous definition”。从类型安全的角度来考虑,“视enum和int为不相容类型”是更妥当的,毕竟以下enum Color代表的取值范围实际上是{0,1,2},而int代表的取值范围则为{…, -1, 0,1,2,3,….}。

生气是拿别人做错的事来惩罚自己

相容类型Compatible Type

相关文章:

你感兴趣的文章:

标签云: