In The C Programming Language (Kernighan and Ritchie, 2nd ed) on p147, the authors show a typedef
declaration
typedef int (*PFI)(char *, char *);
(PFI
stands for "pointer to function returning an int
"), which they claim
can be used in contexts like
PFI strcmp, numcmp;
in the sort program of Chapter 5.
There, strcmp
is either K&R's version from p106¹ or the version from <string.h>
in the standard library², and numcmp
(p121) is a function which converts its arguments to double
s before comparing them numerically:
int strcmp(char *s1, char *s2);
int numcmp(char *s1, char *s2);
¹ It really should be named strcmp_kr
to avoid conflicts with the standard library, but this point is not relevant for this question.
² It technically takes const char *
arguments, but this difference is not relevant for this question.
The sort program they are referring to is the one in section 5.11 (p118-121). (While it is independently problematic (see also here), the reasons have no bearing on this question.)
I understand all that K&R are doing, but I can't make sense of their statement about "contexts like PFI strcmp, numcmp;
". The function signatures of the function designators strcmp
and numcmp
are as stated just above. Adding a declaration like PFI strcmp;
or PFI numcmp;
would lead to a compiler error, as strcmp
and numcmp
are function designators (with function types); they don't have a function pointer type such as PFI
, even though they are automatically converted to function pointers in most contexts (C17 standard draft, 6.3.2.1 ¶4) – which is a subtlety.
That is, I can't come up with a decent example where one can actually write something like PFI strcmp, numcmp;
. Let's try such a thing, but with a simplified toy function lencmp
which compares strings simply by comparing their lengths:
#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 one can equivalently write bare PFI cmp = lencmp;
in the assignment and bare cmp(...)
in the function calls is not what this question is about.)
Well, the above code works and illustrates how one can legally use PFI
. But we wouldn't be able to declare lencmp
like this
PFI lencmp;
because this would lead to an error due to the initial declaration mismatching the later definition of lencmp
.
What do K&R mean by "contexts like PFI strcmp, numcmp;
"? Is this legal? Isn't this simply a semantic error in K&R? Surely one could define variables of the function pointer type PFI
(that is: int (*)(char *, char *)
) and assign strcmp
and numcmp
to them
PFI fp1 = &strcmp;
PFI fp2 = &numcmp;
but we wouldn't be able to name those variables strcmp
and numcmp
. (However, as commenter KamilCuk pointed out, we could rename cmp
within the function main
to one of these names.)
Incidentally, what does work is a typedef
to a direct function type:
typedef int FI(char *, char *);
FI lencmp;
signal
from <signal.h>
.Note that I am not asking about the following:
typedef
itself, which it clear to mef
/*fp
) and function pointers (&f
/fp
) in most contextsI checked the first edition of The C Programming Language (1978), and the answer is:
Kernighan and Ritchie updated the following text from the 1st edition (1e: p141)
For example,
typedef int (*PFI)();
creates the type
PFI
, for "pointer to function returningint
," which can be used in contexts likePFI strcmp, numcmp, swap;
in the sort program of Chapter 5.
in an incomplete/erroneous way for the 2nd edition, which reads (2e: p147):
For example,
typedef int (*PFI)(char *, char *);
creates the type
PFI
, for "pointer to function (of twochar *
arguments) returningint
," which can be used in contexts likePFI strcmp, numcmp;
in the sort program of Chapter 5.
The text in the first edition refers to the following code (1e: sec5.12 (Pointers to Functions), p115; some comments removed):
#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> }
strcmp
,numcmp
andswap
are addresses of functions; since they are known to be functions, the&
operator is not necessary, in the same way that it is not needed before an array name.
(Don't get confused by the part of the last sentence after the semicolon about the missing ampersand &
: this applies to the code in the body of main
(not shown here) and equally applies in the 2nd ed; it is not relevant for the issue discussed in this post.)
The corresponding text in the 2nd edition (2e: sec5.11 (Pointers to Functions), p119) has strcmp
and numcmp
declared (not within main
but) at file scope, and swap
appears (not in that code excerpt but) only later when it is called from their qsort
(p120) and when it is defined (p121).
That is, the line PFI strcmp, numcmp, swap;
from their 1st ed is intended to replace the main
-internal declarations
int strcmp(), numcmp();
int swap();
and the corresponding version PFI strcmp, numcmp;
from their 2nd ed doesn't make sense; it should have been taken out entirely.
The implication from the 1st ed text is that declaring the functions strcmp
/numcmp
/swap
with a function pointer type was acceptable in K&R C. Whether this was truly so or there is a mistake on p141 (1e) is something that I can't judge.