c语言 - 解释此 gcc 函数的类型化行为



在我的代码中,我必须使用几个函数作为lambdas,例如,您应该提供给qsort
所以当我传递int类型的函数时,程序运行良好。但当我还添加了double类型的另一个功能时,出现了错误消息:

1.c:44:29:错误:二进制<(有"双*"one_answers"双")

行:

return (*getter)(a) < target

其中getter是指向的指针

double get_sex(struct human* a) { // it's the second function I've passed
return a->sex;
}

我传递的两个函数之间的唯一区别是,第一个函数是int,第二个是double

sometype somefunction (some parameters,
int *comparator(struct human*, double, double *(struct human*)),
double *getter(struct human*) ) {
....
}

我开始用sizeof检查它,发现代码(*getter)(*a)以某种方式返回了4个字节,而不是8个字节,所以它必须是一个指针,而不是双字节。这就是我收到错误信息的原因
我在维基百科上找了一个例子,发现了额外的()。我已经添加了它们,现在它返回8个字节,工作正常。

double (*getter)(struct human*) ) {

所以问题是:为什么我应该在getter周围加括号,而不是在comparator周围加括号?函数返回double而不是int的原因是什么
这是我从未听说过的语法细节。

(我使用的是在我的Windows上已经安装的编译器——来自Perl解释器Strawberry)

您的问题来自于:

double *getter(struct human*)

隐式转换为:

double *(*getter)(struct human*)

这就是为什么您会出现错误,因为您无法比较(double *)double

您对int没有一些问题,因为您的指针(int *)被强制转换为int,并且可以进行比较。但是,编译器应该警告您从指针到整数的隐式转换。

这里的问题是,我从未见过您可以用这种方式声明函数指针参数。我试着写了一段代码,显然它是有效的。然而,这可能是GCC的一些非标准行为。定义函数指针的正确方法是使用括号。即

double (*getter)(struct human*)  /* 'getter' is a pointer to function taking
struct human and returning double */

在此表达式中:

int myfunc( double *getter(struct human*) );

getter具有函数类型(函数类型返回指向双精度的指针)。在此期间:

int myfunc( double (*getter)(struct human*) );

它具有函数指针类型(指向返回双精度的函数类型的指针)。

函数类型和函数指针类型的工作方式基本相同,因为在大多数情况下,函数类型几乎立即衰减为函数指针类型。我相信这就是标准所说的:(C99,6.3.2.1/4):

函数指示符是一个具有函数类型的表达式。除了当它是sizeof运算符或一元&运算符,类型为"函数返回类型"的函数指示符转换为具有类型"指向的表达式函数返回类型"。

因此,您的表达式:

return (*getter)(a) < target;

根据标准,将getter从函数类型衰减为函数指针类型(类型为double * (*)(struct human*)),尽管您需要double (*)(struct human*)

一旦您将函数传递给somefunction,就会收到一个警告,即它们是不兼容的类型,比如:

警告:从不兼容的指针类型传递"somefunction"的参数2

此外,请参阅此答案以了解详细信息,以及此有趣的答案。

您没有提供所有的代码,所以我猜您正在做的事情类似于:

if (comparator(h, val, getter)) { ... }

其中比较器包括:

return getter(h) < val;

或者类似的东西。

这里的关键是getter的结果由运算符<使用。另一方面,comparator的结果是转换为布尔值(好吧,不是真的:这是C。但这是相同的想法。)并且你可以隐式地将任何指针转换为布尔(即整数),而不会引发任何警告。

所以发生的是:

  1. 比较器返回int,但

  2. 呼叫方正在等待int*。不过没关系;指针和整数以相同的方式返回。

  3. 调用方现在想使用int*,就好像它是int一样,这也是可以的

所以没有错误。

此外,意外强制转换和隐式转换保留了返回值的零度,因此作为奖励,您可以得到正确的答案。

最新更新