Search code examples
clanguage-lawyerfunction-pointerstypedefkernighan-and-ritchie

Are typedef declarations for bare function types (ie: not function pointers) legal in C89/C90?


Let's consider the following code:

#include <stdio.h>
#include <string.h>

typedef int INTFUNC(char *, char *);
INTFUNC lencmp;

int main(void) {
    printf("%d\n", lencmp("a", "aaa")); /* -1 */
    printf("%d\n", lencmp("aaa", "aaa")); /* 0 */
    printf("%d\n", lencmp("aaa", "a")); /* 1 */

    return 0;
}

int lencmp(char *s, char *t) {
    size_t len1 = strlen(s), len2 = strlen(t);
    return (len1 > len2) - (len1 < len2);
}

This compiles fine without warnings with GCC and compiler flags -std=c90 -pedantic -Wall -Wextra.

But K&R (2e) don't show such code in their book; they only ever usetypedefs for function pointers. (Their discussion on p147 is problematic, but that aspect is not the subject of this question.) I don't have access to the C89/C90 standard documents.

According to the C standards documents (C99 TC3 (ISO/IEC 9899:TC3) draft, 6.7.7 ¶7; C11 draft, 6.7.8 ¶7; C17 draft, 6.7.8 ¶7; text identical (modulo formatting) in all three documents):

All three of the following declarations of the signal function [from <signal.h>] specify exactly the same type, the first without making use of any typedef names.

typedef void fv(int), (*pfv)(int);

void (*signal(int, void (*)(int)))(int);
fv *signal(int, fv *);
pfv signal(int, pfv);

For what it's worth, Wikipedia claims:

[example C++ code with typedefs for function pointers:]

[...]
typedef int(Foo::*Foo_pfn)(int,int);
[...]
typedef int(*PFN)(int);
[...]

Alternate C and C++ syntax

The C and C++ syntax [with typedefs for function pointer types] given above is the canonical one used in all the textbooks - but it's difficult to read and explain. Even the above typedef examples use this syntax. However, every C and C++ compiler supports a more clear and concise mechanism to declare function pointers: use typedef, but don't store the pointer as part of the definition. Note that the only way this kind of typedef can actually be used is with a pointer - but that highlights the pointer-ness of it.

C and C++

[...]
typedef int Fn(char c);
[...]
typedef Fn *PFn;
[...]

C++

[...]

Their wording ("canonical" vs "every C and C++ compiler supports") can be understood to have the discursive/conversational implicature that allowing typedefs for bare function types is a compiler extension and not sanctioned by the standards. But is that really so? (One commenter argues that such an implicature is absent.) My quote from the C99 TC3, C11, and C17 standards clearly demonstrates that such typedefs are legal at least since C99 TC3. Given that K&R (2e) doesn't contain typedef examples in this style, I wonder: Are typedef declarations for bare function types (ie: not function pointers) legal in C89/C90? Note that the example "given above" that Wikipedia refers to (not shown in its entirety here) is a C++ example, so it's also possible that it's always been legal in standard C but just not in (some versions of) C++.

Finally, if Wikipedia's characterization of using typedefs for function pointers as "canonical" is correct, the reason for that might lie in the conspicuous lack of typedefs for bare function types in K&R (2e).


Solution

  • Are typedef declarations to bare function types (ie: not function pointers) legal in C89/C90?

    YES.

    See C89 standard:

    3.7.1 Function definitions

    Syntax

         function-definition:
                 declaration-specifiers<opt> declarator
                           declaration-list<opt> compound-statement
    

    Constraints

    The identifier declared in a function definition (which is the name of the function) shall have a function type, as specified by the declarator portion of the function definition./70/

    Footnote 70:

    The intent is that the top type in a function definition cannot be inherited from a typedef:

    typedef int F(void); /* type F is ``function
    of no arguments returning int '' */
    F f, g; /* f and g both have type
    compatible with F */
    F f { /*...*/ } /* WRONG: syntax/constraint error
    */
    F g() { /*...*/ } /* WRONG: declares that g returns a function */
    int f(void) { /*...*/ } /* RIGHT: f has type compatible with F */
    int g() { /*...*/ } /* RIGHT: g has type compatible with F */
    F *e(void) { /*...*/ } /* e returns a pointer to a function */
    F *((e))(void) { /*...*/ } /* same: parentheses irrelevant */
    int (*fp)(void); /* fp points to a function that has type F */
    F *Fp; /* Fp points to a function that has type F */
    

    The first line proves that the typedefing function types is accepted by C89 standard.