关于C中函数指针的一个问题
-
09-09-2019 - |
题
有以下声明:
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”,这是所要求的类型由该方法签名。
两者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因此奇铸造!