在 The C Programming Language(Kernighan and里奇,2nd艾德)第147页中,作者展示了一个typedef
声明
typedef int (*PFI)(char *, char *);
字符串
(PFI
代表“返回int
的函数的指针”),他们声称
可以在以下环境中使用
PFI strcmp, numcmp;
型
在第5章的排序程序中。
其中,strcmp
是K&R的p106¹版本,或者是标准库²中<string.h>
的版本,numcmp
(p121)是一个函数,它将参数转换为double
s,然后进行数字比较:
int strcmp(char *s1, char *s2);
int numcmp(char *s1, char *s2);
型
¹它确实应该被命名为strcmp_kr
,以避免与标准库冲突,但这一点与这个问题无关。
²从技术上讲,它需要const char *
参数,但这个差异与这个问题无关。
他们所指的排序程序是第5.11节中的程序。(虽然it is independently problematic(另见here),但原因与此问题无关。
我理解K&R所做的一切,但我无法理解他们关于“像PFI strcmp, numcmp;
这样的上下文”的声明。函数标志符strcmp
和numcmp
的函数签名如上所述。添加像PFI strcmp;
或PFI numcmp;
这样的声明将导致编译器错误,因为strcmp
和numcmp
是函数指示符(具有函数类型);它们没有像PFI
这样的函数指针类型,尽管它们在大多数上下文中会自动转换为函数指针(C17标准草案,6.3.2.1 ¶4)-这是一个微妙之处。
也就是说,我不能拿出一个像样的例子,其中一个实际上可以写的东西,如PFI strcmp, numcmp;
。让我们尝试这样的事情,但是使用一个简化的玩具函数lencmp
,它通过比较字符串的长度来比较字符串:
#include <stdio.h>
#include <string.h>
typedef int (*PFI)(char *, char *);
int lencmp(char *, char *);
int main(void) {
PFI cmp = &lencmp;
printf("%d\n", (*cmp)("de", "abc")); /* -1 */
printf("%d\n", (*cmp)("def", "abc")); /* 0 */
printf("%d\n", (*cmp)("def", "ab")); /* 1 */
return 0;
}
int lencmp(char *s, char *t) {
size_t len1 = strlen(s), len2 = strlen(t);
return (len1 > len2) - (len1 < len2);
}
型
(That一个人可以等价地在赋值中写裸PFI cmp = lencmp;
,在函数调用中写裸cmp(...)
,这不是这个问题的重点。
好了,上面的代码工作并说明了如何合法地使用PFI
。但是我们不能像这样声明lencmp
PFI lencmp;
型
因为这将由于初始声明与lencmp
的后来定义不匹配而导致错误。
**K&R所说的“像PFI strcmp, numcmp;
这样的上下文”是什么意思?**这是法律的的吗?这不是简单的K&R中的语义错误吗?当然,可以定义函数指针类型PFI
的变量(即:int (*)(char *, char *)
),并将strcmp
和numcmp
分配给它们
PFI fp1 = &strcmp;
PFI fp2 = &numcmp;
型
但是我们不能命名这些变量strcmp
和numcmp
。(然而,正如评论者KamilCuk所指出的,我们可以将函数main
中的cmp
重命名为这些名称之一。
顺便说一句,真正起作用的是typedef
到直接函数类型:
typedef int FI(char *, char *);
FI lencmp;
型
- 有关此样式的官方示例,请参阅C17标准草案(6.7.8 ¶7),该草案将
signal
从<signal.h>
中删除。 - 考虑到这种风格was already legal in C89/C90,令人惊讶的是K&R没有在他们的书中使用它。
请注意,我不是在问以下问题:
2条答案
按热度按时间wr98u20j1#
对标题问题的简洁回答(“K&R写
PFI strcmp, numcmp;
,其中PFI的类型定义为int (*)(char *, char *)
,这真的法律的吗?”)为“是”。然而,这取决于定义出现的位置,它可能不是预期的,所以答案更像是“是的,但是……”。
在函数内部,
PFI strcmp, numcmp;
声明了两个未初始化的函数指针,并隐藏了任何拼写相同的外部函数名。一旦初始化为指向适当函数的非空指针,它们就可以像任何其他函数指针一样使用。但是,请注意,没有简单的方法可以使它们指向函数strcmp()
或numcmp()
-这些名称已被隐藏。(你可以通过将指针变量的地址传递给一个分配正确指针的函数来实现,但这相当迂回。在函数之外,它们仍然是函数指针,它们应该与链接到程序中的对象文件中具有相同名称的任何函数冲突。
strcmp
这个名字可能会使试图使用标准库strcmp()
函数的程序复杂化,而且很多程序都属于这一类。不能使用typedef作为函数的类型来声明函数。定义:
字符串
不等于:
型
(with或者没有可选的
extern
存储类)。因此,我认为K&R在P147上的主张是可疑的-要么缺乏足够的背景来表明他们在想什么,要么实际上是错误的。
我忽略了
strcmp()
的标准C类型与书中K&R使用的类型不匹配的问题。这基本上与问题无关。deyfvvtc2#
我查了一下《C编程语言》(1978年)的第一版,答案是:
Kernighan和里奇更新了第1版(1 e:p141)
例如,在一个示例中,
字符串
创建类型
PFI
,表示“指向返回int
的函数的指针”,它可以在以下环境中使用:型
在第5章的排序程序中。
以不完整/错误的方式用于第2版,其内容为(2 e:第147页):
例如,在一个示例中,
型
创建类型
PFI
,用于“指向返回int
的函数(两个char *
参数)的指针”,它可以在以下环境中使用:型
在第5章的排序程序中。
第一版中的文本参考以下代码(1 e:第5.12节(函数指针),第115页;删除部分评论):
型
strcmp
、numcmp
和swap
是函数的地址;因为已知它们是函数,所以&
运算符不是必需的,就像在数组名之前不需要它一样。(Don不要被最后一句中分号后面关于缺失的&符号
&
的部分弄糊涂了:这适用于main
的主体中的代码(这里未示出),并且同样适用于第2艾德;这与本文讨论的问题无关。)第2版(2 e:sec5.11(函数指针),p119)在文件范围内声明了
strcmp
和numcmp
(不在main
内,但),而swap
(不在代码摘录中,但)仅在稍后从qsort
调用时(p120)和定义时(p121)出现。也就是说,第1艾德中的
PFI strcmp, numcmp, swap;
行旨在替换main
-internal声明型
和**对应的版本
PFI strcmp, numcmp;
从他们的第二艾德没有意义;它应该被完全取出。第1艾德文本的含义是,在K&R C中,使用函数 * 指针 * 类型声明 * 函数 *
strcmp
/numcmp
/swap
是可以接受的。这是真的还是在p141(1 e)上有错误,我无法判断。