K&R写“PFI strcmp,numcmp;“其中PFI被定义为“int(*)(char *,char *)"?

yshpjwxd  于 2023-08-03  发布在  其他
关注(0)|答案(2)|浏览(104)

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;这样的上下文”的声明。函数标志符strcmpnumcmp的函数签名如上所述。添加像PFI strcmp;PFI numcmp;这样的声明将导致编译器错误,因为strcmpnumcmp是函数指示符(具有函数类型);它们没有像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 *)),并将strcmpnumcmp分配给它们

PFI fp1 = &strcmp;
PFI fp2 = &numcmp;


但是我们不能命名这些变量strcmpnumcmp。(然而,正如评论者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没有在他们的书中使用它。

请注意,我不是在问以下问题:

wr98u20j

wr98u20j1#

对标题问题的简洁回答(“K&R写PFI strcmp, numcmp;,其中PFI的类型定义为int (*)(char *, char *),这真的法律的吗?”)为“是”。
然而,这取决于定义出现的位置,它可能不是预期的,所以答案更像是“是的,但是……”。
在函数内部,PFI strcmp, numcmp;声明了两个未初始化的函数指针,并隐藏了任何拼写相同的外部函数名。一旦初始化为指向适当函数的非空指针,它们就可以像任何其他函数指针一样使用。但是,请注意,没有简单的方法可以使它们指向函数strcmp()numcmp()-这些名称已被隐藏。(你可以通过将指针变量的地址传递给一个分配正确指针的函数来实现,但这相当迂回。
在函数之外,它们仍然是函数指针,它们应该与链接到程序中的对象文件中具有相同名称的任何函数冲突。strcmp这个名字可能会使试图使用标准库strcmp()函数的程序复杂化,而且很多程序都属于这一类。
不能使用typedef作为函数的类型来声明函数。定义:

PFI strcmp, numcmp;

字符串
不等于:

extern int strcmp(char *s1, char *s2);
extern int numcmp(char *s1, char *s2);


(with或者没有可选的extern存储类)。
因此,我认为K&R在P147上的主张是可疑的-要么缺乏足够的背景来表明他们在想什么,要么实际上是错误的。
我忽略了strcmp()的标准C类型与书中K&R使用的类型不匹配的问题。这基本上与问题无关。

deyfvvtc

deyfvvtc2#

我查了一下《C编程语言》(1978年)的第一版,答案是:
Kernighan和里奇更新了第1版(1 e:p141)
例如,在一个示例中,

typedef int (*PFI)();

字符串
创建类型PFI,表示“指向返回int的函数的指针”,它可以在以下环境中使用:

PFI strcmp, numcmp, swap;


在第5章的排序程序中。

以不完整/错误的方式用于第2版,其内容为(2 e:第147页):

例如,在一个示例中,

typedef int (*PFI)(char *, char *);


创建类型PFI,用于“指向返回int的函数(两个char *参数)的指针”,它可以在以下环境中使用:

PFI strcmp, numcmp;


在第5章的排序程序中。
第一版中的文本参考以下代码(1 e:第5.12节(函数指针),第115页;删除部分评论):

#define LINES 100

main(argc, argv)    /* sort input lines */
int argc;
char *argv[];
{
     char *lineptr[LINES];
     int nlines;
     int strcmp(), numcmp(); /* comparison functions */
     int swap();   /* exchange function */
     numeric = 0;
     
     <body of function>
}


strcmpnumcmpswap是函数的地址;因为已知它们是函数,所以&运算符不是必需的,就像在数组名之前不需要它一样。
(Don不要被最后一句中分号后面关于缺失的&符号&的部分弄糊涂了:这适用于main的主体中的代码(这里未示出),并且同样适用于第2艾德;这与本文讨论的问题无关。)
第2版(2 e:sec5.11(函数指针),p119)在文件范围内声明了strcmpnumcmp(不在main内,但),而swap(不在代码摘录中,但)仅在稍后从qsort调用时(p120)和定义时(p121)出现。
也就是说,第1艾德中的PFI strcmp, numcmp, swap;行旨在替换main-internal声明

int strcmp(), numcmp();
int swap();


和**对应的版本PFI strcmp, numcmp;从他们的第二艾德没有意义;它应该被完全取出。
第1艾德文本的含义是,在K&R C中,使用函数 * 指针 * 类型声明 * 函数 * strcmp/numcmp/swap是可以接受的。这是真的还是在p141(1 e)上有错误,我无法判断。

相关问题