有以下声明:
void qsort(void *lineptr[], int left, int right, int (*comp)(void *, void *));
int numcmp(char *, char *);
int strcmp(char *s, char *t);
然后,在程序的某个地方有以下调用:
qsort((void**) lineptr, 0, nlines-1,
(int (*)(void*,void*))(numeric ? numcmp : strcmp));
(忽略前三个参数和numeric
)。
我问这是什么:
(int (*)(void*,void*))(numeric ? numcmp : strcmp)
我理解qsort
期待一个“指向函数的指针获得两个void
指针并返回一个int
”,因为它是第四个参数,但上面的内容是如何满足的?
在我看来,它似乎是某种类型的演员,因为它是由两个括号组成的,但那将是一个非常奇怪的演员。因为它需要一个函数并使该函数成为“指向函数的指针,该指针获得两个void
指针并返回int
”。这没有意义。
(我在这里遵循的规则是,在变量将变量提升为该类型之前,括号中的 type
类型。)
所以我想我错了,也许有人可以告诉我怎么读这个,订单是什么?
答案 0 :(得分:5)
你错过了这里的诀窍 - 部分
(numeric ? numcmp : strcmp)
使用三元运算符选择在qsort内调用哪个函数。如果数据是数字,则使用numcmp。如果没有,它使用strcmp。更具可读性的实现如下所示:
int (*comparison_function)(void*,void*) =
(int (*)(void*,void*))(numeric ? numcmp : strcmp);
qsort((void**) lineptr, 0, nlines-1, comparison_function);
答案 1 :(得分:4)
这里发生的事情确实是演员阵容。让我们忽略三元并假装总是使用numcmp。出于这个问题的目的,函数可以充当C中的函数指针。因此,如果你看一下数字的类型,它实际上是
(int (*)(int*,int*))
为了在qsort中正确使用它,它需要有void参数。因为这里的类型在参数和返回类型方面都具有相同的大小,所以可以替换另一个类型。所需要的只是一个使编译器满意的演员表。
(int (*)(void*,void*))(numcmp )
答案 2 :(得分:4)
您可以在不使用函数指针的情况下执行此操作。这是how。根据我的经验,在大多数地方,如果你使用的是演员,那你就错了。
答案 3 :(得分:3)
请注意qsort()
的标准定义包括const
:
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
请注意,字符串比较器有两个“char **
”值,而不是“char *
”值。
我编写比较器,以便在调用代码中不需要强制转换:
#include <stdlib.h> /* qsort() */
#include <string.h> /* strcmp() */
int num_cmp(const void *v1, const void *v2)
{
int i1 = *(const int *)v1;
int i2 = *(const int *)v2;
if (i1 < i2)
return -1;
else if (i1 > i2)
return +1;
else
return 0;
}
int str_cmp(const void *v1, const void *v2)
{
const char *s1 = *(const char **)v1;
const char *s2 = *(const char **)v2;
return(strcmp(s1, s2));
}
强迫人们使用你的函数在代码中编写强制转换是很难看的。不。
我写的两个函数匹配标准qsort()
所需的函数原型。未跟随括号的函数名称相当于指向函数的指针。
您可以在较旧的代码中找到代码,或者由那些在较旧的编译器上编写的代码编写的代码,使用符号来使用指向函数的指针:
result = (*pointer_to_function)(arg1, arg2, ...);
以现代风格写成:
result = pointer_to_function(arg1, arg2, ...);
就个人而言,我发现明确的解除引用更清楚,但不是每个人都同意。
答案 4 :(得分:3)
正如其他人所指出的那样,
(int (*)(void*,void*))(numeric ? numcmp : strcmp)
然后以下是类型转换
(int (*)(void*,void*))
,表达式为
(numeric ? numcmp : strcmp)
C声明可能很难阅读,但可以学习。方法是从内部开始,然后向右一步,然后向左一步,向右,向左,向右,向左等,直到完成。在评估内部的所有内容之前,不要在括号外穿过。例如,对于上面的类型转换,(*)
表示这是一个指针。指针是括号内唯一的东西,所以我们在它之外的右侧进行评估。 (void*,void*)
表示这是一个指向具有两个指针参数的函数的指针。最后int
表示函数的返回类型。外括号使其成为类型转换。
更新:两篇更详细的文章:The Clockwise/Spiral Rule和Reading C Declarations: A Guide for the Mystified。
然而,好消息是虽然以上内容非常有用,但有一种非常简单的欺骗方式: cdecl 程序可以从C转换为英文描述,反之亦然:
cdecl> explain (int (*)(void*,void*))
cast unknown_name into pointer to function (pointer to void, pointer to void) returning int
cdecl> declare my_var as array 5 of pointer to int
int *my_var[5]
cdecl>
练习:i
是什么类型的变量?
int *(*(*i)[])(int *)
如果您的计算机上没有安装cdecl,请在rot13中回答(但您真的应该这样做):
pqrpy> rkcynva vag *(*(*v)[])(vag *)
qrpyner v nf cbvagre gb neenl bs cbvagre gb shapgvba (cbvagre gb vag) ergheavat cbvagre gb vag
pqrpy>
答案 5 :(得分:3)
编写该代码段的人试图过于聪明。在他看来,他可能认为自己是一个优秀的程序员,通过制作一个聪明的“一线”。 实际上,他制作的代码可读性较差且长期使用是令人讨厌的,应该以类似于Harper Shelby代码的更明显的形式进行重写。
记住Brian Kernighan的格言:
调试是写入的两倍 代码首先。 因此,如果您将代码编写为 尽可能巧妙地,你是 定义,不够智能调试 它
我在硬实时期限内做了很多性能关键编码......而且我还没有看到一个密集的单线程适合的地方。
我甚至搞砸了编译和检查asm以查看单行程序是否有更好的编译asm实现,但从未发现单行程值得它。
答案 6 :(得分:1)
我可能会这样读:
typedef int (*PFNCMP)(void *, void *);
PFNCMP comparison_function;
if (numeric)
{
comparison_function = numcmp;
}
else
{
comparison_function = strcmp;
}
qsort((void**) lineptr, 0, nlines-1, comparison_function);
问题中的示例有一个明确的案例。
答案 7 :(得分:0)
我认为你的逻辑是正确的。它实际上是“指向函数的指针,它获取两个void指针并返回一个int”,这是方法签名所需的类型。
答案 8 :(得分:0)
numcmp
和strcmp
都是指向函数的指针,这些函数将两个char*
作为参数并返回int
。 qsort
例程需要一个指向函数的指针,该函数将两个void*
作为参数并返回int
。因此演员。这是安全的,因为void*
充当通用指针。现在,继续阅读声明:让我们来你的 strcmp
的声明:
int strcmp(char *, char *);
编译器将其读取为strcmp
实际上是:
int (strcmp)(char *, char *)
一个函数(在大多数情况下衰减到指向函数的指针),它带有两个char *
个参数。因此指针strcmp
的类型为:
int (*)(char *, char *)
因此,当您需要转换另一个与strcmp
兼容的函数时,您可以使用上面的类型来转换为。
同样,由于qsort
的比较器参数需要两个void *
s,因而是奇数演员!