See Also: part 1, part 2, part 3, part 4 and part 5.
This post is a departure from what most of the others are like. I am most certainly not going to be using this “trick” I just learned.
Background
Recently in my day job, I was doing a code review and came across something that was most certainly not legal C code. In fact, I was confident that line wouldn’t even compile without issuing a warning. Yet, the developer said he did not see a warning about it.
The bit of code was supposed to be calling a function that returns a populated structure. Consider this silly example:
#include <stdio.h>
// typedefs
typedef struct {
unsigned int major;
unsigned int minor;
unsigned int patch;
} VersionStruct;
// prototypes
VersionStruct GetVersion (void);
// main
int main()
{
VersionStruct foo;
foo = GetVersion ();
printf ("Version %u.%u.%u\n", foo.major, foo.minor, foo.patch);
return 0;
}
// functions
VersionStruct GetVersion ()
{
VersionStruct ver;
ver.major = 1;
ver.minor = 0;
ver.patch = 42;
return ver;
}
But in the code, the call to the function was incomplete. There was no return variable, and even had “void” inside the parens. It looked something like this:
int main()
{
VersionStruct GetVersion (void);
return 0;
}
I took one look at that and said “no way that’s working.” But there had been no compiler warning.
So off I went to the Online GDB Compiler to type up a quick example.
And it built without warning.
Well, maybe the default is to ignore this warning… So I added “-Wall” and “-Wextra” to the build flags. That should catch it :)
And it built without warning.
“How can this work? It looks like a prototype in the middle of a function!” I asked.
Yes, Virginia. You can have inline prototypes.
A brief bit of searching told me that, yes, inline prototypes were a thing.
This should give a compiler warning:
#include <stdio.h>
int main()
{
function ();
return 0;
}
void function (void)
{
printf ("Inside function.\n");
}
When I built that, I received two compiler warnings:
main.c: At top level:
main.c:10:6: warning: conflicting types for ‘function’; have ‘void(void)’
10 | void function (void)
| ^~~~~~~~
main.c:5:5: note: previous implicit declaration of ‘function’ with type ‘void(void)’
5 | function ();
| ^~~~~~~~
The first warning is not about the missing prototype, but about “conflicting types”. In C, a function without a prototype is assumed to be a function that returns an int.
Had I made function like this…
int function (void)
{
printf ("Inside function.\n");
return 0;
}
…I’d see only one, but different, warning:
main.c: In function ‘main’:
main.c:5:5: warning: implicit declaration of function ‘function’ [-Wimplicit-function-declaration]
5 | function ();
| ^~~~~~~~
For the first example, the compiler makes an assumption about what this function should be, then finds code using it the wrong way. It warns me that I am not using it like the implied prototype says it should be used. Sorta.
For the next, my function matches the implied prototype, so those warnings go away, but a real “implicit declaration” warning is given.
Going back to the original “void” code, I can add an inline prototype in main() to make these all go away:
#include <stdio.h>
int main()
{
void function(void); // Inline prototype?
function ();
return 0;
}
void function (void)
{
printf ("Inside function.\n");
}
I had no idea that was allowed.
I have no idea why one would do that. BUT, I suppose if you wanted to get to one function without an include file with a prototype for it, you could just stick that right before you call the function…
But Why would you want to do that?
I learned this is possible. I do not think I want to ever do this. Am I missing some great benefit for being able to have a prototype inside a function like this? Is there some “clean code” recommendation that might actually say this is useful?
“It wouldn’t be in there if it didn’t have a reason.”
Let me know what you know in the comments. Until next time…
Yup, function prototypes inside a function is valid. So are struct, union and typedef declarations. As an aside, I always mark function prototypes with
extern
to make them stand out (it’s not required, but I like to have them). You can even declare external variables inside function blocks. I’ve done that a few times, mostly withextern char **environ
to point to the global environment block within a function that needs it. The only thing you can’t do is have nested functions. It would be nice, but I understand why they’re disallowed in C.extern foo(int); ??? I wonder what the logic behind having extern is there – does it change anything? Or maybe it is like LET in BASICs that support it but it is not required?
Having
extern
there doesn’t change anything, but I like being explicit about things.