Old C dog, new C tricks part 5: inline prototypes?

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…

3 thoughts on “Old C dog, new C tricks part 5: inline prototypes?”

  1. Sean Patrick Conner

    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 with extern 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.

    Reply ↓
    1. Allen Huffman Post author

      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?

      Reply ↓
      1. Sean Patrick Conner

        Having extern there doesn’t change anything, but I like being explicit about things.

        Reply ↓

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.