在K&R ANSI C书中,我偶然发现了一段代码,其中使用了指向函数的指针。我想我理解函数指针背后的思想,但是书中的例子对我来说没有意义。
功能是对文本行进行排序,使用快速排序算法. quicksort函数的声明如下所示:
void qsort(void *lineptr[], int left, int right, int (*comp)(void *, void *);
字符串
到目前为止,这对我来说是完全有意义的,我们正在声明一个函数,其中一个参数是一个指向函数(comp)的指针,其中有两个void 指针参数。我假设我们这样做是为了保存内存,而不是复制实际的函数。
之后,函数在main中以这种方式使用:
qsort((void **) lineptr, 0, nlines-1, (int (*)(void*, void*))(numeric ? numcmp : strcmp));
型
这里是我有一些问题的地方:
1.为什么lineptr
转换为(void**)
1.使用(numeric ? numcmp : strcmp)
有什么用,是否意味着指向函数的指针也可以这样赋值:int ( * )(void*, void*) numcmp
个
编辑:lineptr
的定义如下:char *lineptr[MAXLINES];
我假设我们正在强制转换void**
以将其从char类型更改为void指针类型
4条答案
按热度按时间ql3eal8s1#
K&R的书早于标准化的C语言,所以一些在当时有效的结构不再有效。具体来说,它似乎是在假设任何两个指针类型之间的转换都是有效的。
lineptr
是char *
的数组,当在表达式中使用时,这个数组将衰减为指向其第一个元素的指针,即char **
。因此需要转换为void **
以匹配参数类型,因为只有void *
的转换可以在没有转换的情况下执行。表达式
(numeric ? numcmp : strcmp)
选择一个函数作为comp
参数传递给该函数,因此numcmp
或strcmp
取决于numeric
的值。需要强制转换为(int (*)(void*, void*))
,因为strcmp
(可能还有numcmp
)的类型为int (*)(const char *, const char *)
。在标准C中,转换为不同的函数指针类型并随后使用转换的类型调用函数是undefined behavior,但在K&R C中可能允许。
2g32fytz2#
K&R(Brian W Kernighan and Dennis M里奇e0f1x 1988)中的这段代码不符合标准C语言,也不是当今编写代码的方式。
这是一个严重的指控-但C11标准说(§6.3转换,特别是在§6.3.2.3指针中)8:
指向一种类型的函数的指针可以转换为指向另一种类型的函数的指针,然后再转换回来;结果将与原始指针相等。如果转换后的指针用于调用其类型与引用类型不兼容的函数,则行为未定义。
在这本书中,
qsort()
函数的签名是:字符串
这与标准C
qsort()
的接口不同,并且如履薄冰-您应该避免重新定义标准C函数,特别是如果重新定义具有不同的接口。理想情况下,该函数将被重命名,可能是quicksort()
。因此,作为
comp
传递的函数指针应该与签名匹配型
但这两个函数具有签名:
型
qsort()
内部的代码将函数指针视为int (*comp)(void *, void *)
,因此根据§6.3.2.3 ¶8,代码具有未定义的行为。转换到公共类型是丑陋的,但必须满足调用qsort()
的要求,并且可以通过转换回被调用函数(qsort()
)中的原始类型来撤销,但它不会转换函数指针。为了满足现代C语言的要求,
numcmp()
函数需要重写:型
修改后的
strcmp()
也将被类似地重写-并重命名以避免与strcmp()
的标准C实现冲突。这样,对qsort()
的调用就不需要对comparator参数进行强制转换。仍然需要对数组指针参数进行强制转换,因为char **
不会自动转换为void **
(但会转换为void *
)。现在,这是理论观点-由C标准支持。然而,在实践中,你经常会逃脱这种滥用(但编译器可能会生成关于滥用的警告)。
在标准C中,
qsort()
函数的原型是:型
这在几个方面是不同的,特别是比较器被传递
const
指针,数组是void *
而不是书中的void **
。上面的
numcmp()
函数必须重写。如果你正在对一个字符串数组(char **
)进行排序,这就是这本书所做的,那么参数的类型是char **
转换为void *
。下面是一些对一个行数组进行排序的代码,使用POSIX
getline()
读取行。这段代码可以在我的SOQ(Stack Overflow Questions)仓库中找到,在GitHub的src/sortfile子目录下的sortlines2.c
文件中;还有一个X1 M25 N1 X,它的存储效率更高。这段代码让getline()
为每一行分配新的内存,它很乐意这样做,但它分配的最小内存大小通常远大于您正在阅读的行。型
请注意,
qsort()
的参数没有强制转换-它们应该是不必要的。要编写数字比较器,您需要:
型
然后简单地将
numcmp
(不进行强制转换)传递给qsort()
。iqjalb3h3#
qsort
调用使用了2个C惯用点:(numeric ? numcmp : strcmp)
实际上是(numeric ? &numcmp : &strcmp)
这里我们有:
numeric
为True,则将numcmp
用作比较函数,并将&numcmp
转换为指向函数的指针,该函数取2个void *
并返回int
numeric
为False,则使用strcmp
作为比较函数,并将&strcmp
转换为指向函数的指针,该函数取2个void *
并返回int
对于第一个问题,
lineptr
被转换为指向void的指针,因为它是qsort
所期望的类型。这里的规则是,指向对象的指针可以转换为指向另一种类型(对象)的指针,当转换回其真实的类型时,它将获得其原始值。用错误的类型解除引用它是唯一的错误。ncgqoxb04#
为什么不这样做呢:
第一个月
一米
qsort
需要处理某种类型的数组。它不能是void
的数组。没有这样的事,虚空也不能比较。对于泛型函数,该数组必须是void
指针数组。所以这里需要void *lineptr[]
akavoid **lineptr
:指向多个指针中的第一个的指针。(int (numeric ? &numcmp : &strcmp)(void, void*))
嗯,这是不编译。
numeric ? &numcmp : &strcmp
你知道一个用作指针的数组如何退化为指向其第一个元素的指针吗?一个函数被用作指针,它会退化为一个指向函数的指针。因此,当用作参数时,
numeric ? numcmp : strcmp
等效于numeric ? &numcmp : &strcmp
。