C: Stop writing f()

07 May 2022

It's not uncommon for me to check out the C source code of some program that I use and find lots of functions declared like this f() rather than f(void). Three main reasons that I can think of why people do this are:

  1. It's intuitive, obviously f() is a function that takes no arguments! Why would I want to type out void?
  2. Lots of introductory books (including K&R 2nd edition itself! ) use this old style declaration.
  3. Programmers coming from C++ where f() and f(void) are equivalent.

So... what's wrong with using f() in C? You may have already guessed it from point 1 and 3, but f() and f(void) in C are not equivalent.

What this means in practice is that when you declare your function like f(void), the compiler can and will type check it to make sure you're not accidentally calling it like f("string"); or f(5);. And if you are, the error will be caught at compile time.

But since f() says the function takes unspecified amount of arguments the compiler cannot check weather the arguments you provide it are valid or not. And worse, if you do end up making a mistake when calling such a function, it will trigger undefined behavior.

static void f();
static void f2(void);

int main(void)
{
    f("string"); /* this will compile fine */
    f(69.420); /* so will this */
    f2(5); /* compile time error */
}
/* dummy definition */
void f()      { return; }
void f2(void) { return; }

Worst case scenario, you may end up silently corrupting your stack. These type of bugs, when they do occur, are pretty hard to debug. So it's best to rule out the possibility of them happening all together.


K&R 2nd edition did contain a strong warning not to use old-style function declaration near the end of chapter 1.7.


-Wstrict-prototypes to the rescue

One of the easiest to make sure you're not using old style declaration is to use the warning flag -Wstrict-prototypes, which is supported both by GCC and Clang. Additionally you could (and should) also enable -Wold-style-definition.

Unfortunately, neither of these flags are enabled by default. Nor are they enabled when using -Wall or -Wextra. So you'll have to enable them manually.

But why are f() and f(void) different?

Short answer: Legacy braindeath.

Long answer: It's because pre-standard C (also called K&R C) did not have typed functions. So it was normal to have code like this:

/* file.h */
extern void f();

/* file.c */
void
f(argc, argv)
    int argc;
    char **argv;
{
    /* do stuff */
}

Here f is a function that takes an int and a char ** as argument. However there's no way to specify that in the prototype and it's up to the caller to make sure he's calling the function properly.

When C89 (aka ANSI C) was standardized, making sure existing code worked was one of the priority. And because of that it was decided that f() will continue to behave in legacy style where f(void) would declare a no argument function.

This however was changed in C99, where old style declaration has been marked obsolete.

Some (questionable) usage of f()

One valid use of the old style declaration might be where you're trying to escape the type system. For example, you might want to have a function pointer fp which might either hold close (3) or fclose (3), both of which return an int but take different type of arguments.

int close(int fd);
int fclose(FILE *stream);

In this case, it's possible to use an old style declaration to get around the type system:

int (*fp)();
fp = close;
fp = fclose;

But this is probably not the best example because in this case, you could've (and should've) simply used a union:

union {
    int (*c)(int);
    int (*fc)(FILE *);
} u;
u.c = close;
u.fc = fclose;

And given the fact that old style declaration are obsolete since C99, it's best avoided entirely on new codebases with the help of -Wstrict-prototypes and -Wold-style-definition.

Wrapping it up

I wrote this so I can use it as quick reference in the future since I see people confusing f() with f(void) pretty often. Feel free to contact me via email if you have any improvement or correction.



RSS Feed