有以下声明:

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 在变量之前的括号中将变量提升为该类型)。

所以我想我只是弄错了,也许有人可以告诉我如何阅读这个,顺序是什么?

有帮助吗?

解决方案

这里发生的事情确实是一个演员。让我们忽略第二三元,假装NUMCMP总是使用。对于这个问题的目的,功能可以所以,如果你看看数字的类型,它实际上是在C.函数指针作用

(int (*)(int*,int*))

在为了使其适当地使用在的qsort它需要有空隙的参数。因为这里的类型都具有相同的尺寸相对于参数和返回类型,它可能替代对其他。所有这一切需要的是铸造,使编译器高兴。

(int (*)(void*,void*))(numcmp )

其他提示

您已经在这里错过了绝招 - 部分

(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);

您可以不用函数指针演员。这里是如何。根据我的经验,在大多数地方,如果你使用的是演员,你做错了。

请注意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, ...);

就个人而言,我找到了明确的解引用清晰,但并不是所有人都同意。

编写该代码片段的人太聪明了. 。在他看来,他可能认为通过巧妙的“俏皮话”他就能成为一名优秀的程序员。 事实上,他编写的代码可读性较差,并且长期使用会令人讨厌 并且应该以类似于哈珀谢尔比代码的更明显的形式重写。

请记住布莱恩·科尼汉 (Brian Kernighan) 的格言:

调试是首先写代码的两倍。因此,如果您尽可能巧妙地编写代码,则根据定义,您不足以调试代码。


我在严格的实时期限内进行了大量性能关键的编码......我还没有看到一个地方适合用密集的俏皮话。

我什至尝试过编译和检查 asm,看看单行代码是否有更好的编译 asm 实现,但从未发现单行代码值得这样做。

正如其他人指出的那样,对于

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

,则以下是一种类型的流延

(int (*)(void*,void*))

和表达是

(numeric ? numcmp : strcmp)

C声明可以是相当难以阅读,但有可能学习。该方法是开始在内部部分,然后向右走一步,然后离开了一步,继续右,左,右,左等向外直到完成。内部已评估一切之前,您不要在括号外交叉。例如,对于上述投类型,(*)结果表明是一个指针。指针是在括号内的唯一的事所以后来我们评估右侧外面。 (void*,void*)表明是一个指向具有两个指针参数的函数。最后int表示函数的返回类型。外括号使得这种类型转换。 更新:两个详细的文章:顺时针/螺旋规则和的读C声明:A指南的大惑不解

不过,好消息是,虽然上面的是要知道非常有用的,有欺骗一个非常简单的方法:在 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 *)

答在 ROT13 的情况下,你没有CDECL您的计算机上安装(但你真的应该!):

pqrpy> rkcynva vag *(*(*v)[])(vag *)
qrpyner v nf cbvagre gb neenl bs cbvagre gb shapgvba (cbvagre gb vag) ergheavat cbvagre gb vag
pqrpy>

我可能会读它是这样的:

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);

在讨论的实施例具有一个显式的情况下。

您逻辑正确我想。它确实是铸造为“指针函数得到两张空指针并返回一个int”,这是所要求的类型由该方法签名。

两者numcmpstrcmp是指向需要两个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因此奇铸造!

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top