当前位置: C语言 -- 专题 -- 兼容类型

兼容类型(五)

五、函数类型的兼容性

C语言中函数的声明有两种风格:一种函数声明时指明形式参数的类型,称为函数原型(function prototype);另一种函数声明时不指明函数参数的类型,也称为旧风格(old-style)。

int funcOne(int income);    //函数原型。
int funcTwo();              //旧风格。

funcOne的声明是函数原型;funcTwo的声明是旧风格。


在函数定义中也存在是否包含函数原型的情况。根据ISO/IEC 9899:2018标准第6.11.6 Function declarators节及第6.11.7 Function definitions节,不使用函数原型的函数声明、不使用函数原型的函数定义是一种过时的做法。有鉴于此,本文在讨论函数的兼容性时将不讨论旧风格的函数。


对于函数类型有两个重要特征,一个是函数的返回类型,另一个是参数的数量和类型。如果两个函数类型兼容,要求函数的返回类型兼容,参数的数量相等,对应参数的类型兼容。

int a(int);
int b(int);
int c(int, int);
float d(int);
int e(int, double);

ab的类型是兼容的;ac的类型是不兼容的,因为参数数量不同;ad的类型是不兼容的,因为函数返回类型不同;ae的类型是不兼容的,因为参数数量不同;ce的类型是不兼容的,因为有一个参数类型是不兼容的。


关于函数参数兼容的几种特殊情况:

① 函数参数包含省略号。

省略号(...)作为函数的参数表示函数具有可变数量的参数,两个兼容类型的函数,如果一个使用了省略号,另一个就应该对应的使用省略号。

int a(int, ...);
int b(int, ...);
int c(int, int);

ab的类型是兼容的;abc的类型是不兼容的。


② 函数参数为数组类型。

函数声明时,如果函数参数是数组类型,函数参数与对应的指针类型相兼容。

int a(int *);
int b(int [*]);

ab的类型是兼容的,其中a的参数为一个int类型的指针,b的参数为一个int类型的变长数组。


③ 函数参数为函数类型。

函数声明时,如果函数参数是函数类型,函数参数与对应的指针类型相兼容。

int a(int (void));
int b(int (*)(void));

ab的类型是兼容的,其中a的参数为一个返回类型为int类型没有参数的函数,b的参数为一个指向返回类型为int类型没有参数的函数的指针。


④ 函数参数有类型限定符的情况。

顶级类型限定符(top-level qualifier)是限定对象自身的类型限定符,以const类型限定符为例:

int numberOne = 10;
const int numberTwo = 5;
const int *ptrOne = &numberTwo;
int * const ptrTwo = &numberOne;
const int * const ptrThree = &numberTwo;

如果const是顶级类型限定符,其限定对象的值是不能改变的;其限定对象只能初始化,初始化后不能再被赋值。

对于numberTwo,类型限定符是顶级类型限定符,numberTwo的值是不能改变的;对于ptrOne,类型限定符不是顶级类型限定符,其限定的是指针指向的对象,而不是指针自身,ptrOne指针还可以指向其它实体;对于ptrTwo,类型限定符是顶级类型限定符,其限定的是指针自身,ptrTwo只能指向变量numberOne,而不能指向其它实体,指针的指向位置是不变的;对于ptrThree*号左边的const不是顶级类型限定符,其限定的是指针指向的对象,*号右边的const是顶级类型限定符,其限定的是指针自身。

:顶级类型限定符并不是ISO/IEC 9899:2018标准中的术语。


如果在函数参数中出现了顶级类型限定符,将和没有顶级类型限定符的非限定版本相兼容。

int a(int);
int b(const int);
int c(int *);
int d(int * const);

bd中的const类型限定符均为顶级类型限定符,如果没有顶级类型限定符,ab的参数类型是相同的,cd的参数类型是相同的;所以这里ab的函数类型是兼容的,cd的函数类型是兼容的。