C unnecessary function prototypes

Consider these styles for a single source file program.

Form 1
Code:
void A(int x, int y);
int B(float x);

int main(int argc, const char *argv[])
{
    A(1, 2);
    return B(3.f);
}

void A(int x, int y)
{
    // some code goes here.
}

int B(float x)
{
    // do something with x
    return 0;
}

Form 2
Code:
void A(int x, int y)
{
    // some code goes here.
}

int B(float x)
{
    // do something with x
    return 0;
}

int main(int argc, const char *argv[])
{
    A(1, 2);
    return B(3.f);
}

Form 3
Code:
static void A(int x, int y)
{
    // some code goes here.
}
static int B(float x)
{
    // do something with x
    return 0;[/ICODE]
}

int main(int argc, const char *argv[])
{
   A(1, 2);
   return B(3.f);
}
I personally prefer form 3 above. Form 1 violates the DRY or don't repeat yourself principle. I see quite a bit of code using form 1 which doesn't actually need prototypes and also exports the symbols to the linker instead of marking them as static and file scope. If you add or remove parameters to functions A or B - or change the return type - you need to remember to touch both locations. Why incur the risk?

Unless I have a pair of codependent functions, I can usually avoid the prototype.

As before, no reply needed. I just like to complain. :rolleyes:
 
I prefer form 3 as well, but to each their own. There are people who can't look at their code without getting mad if functions are not sorted alphabetically (for binary search). Others who just love to have references in a legible list and see no reason they could not have that for static functions. Others who laugh at the idea that anyone is taking seriously the fact that functions order matter in C. Live and let live, they do whatever they want in _their_ codebase.

(I find very hard to defend form 2 instead of form 3, though… 😅 But I guess there will be people having reasons for that)
 
Header files, especially if the function is meant to be publicly visible.
Static means "reference does not exist outside this object file" (at least in C, not C++, but it can mean that in C++)
Although B doesn't really need to do anything with X because X is not modifiable in B (pass by value not by reference I think is the correct term)


Don't more current version of C standard let you get form 3 by doing:

int main(int argc, const char *argv[])
{
void A(int x, int y)
{
// some code goes here.
}

int B(float x)
{
// do something with x
return 0;
}
A(1, 2);
return B(3.f);
}
 
static for a function means it can't be used outside the current file, it's private API (and you can define static functions with the same name elsewhere in the codebase). That's also the point of putting the prototype at the top of file rather than in a header file: it allows to have prototypes, not having to bother with the order of functions, while still marking clearly that other files are not supposed to use this function (and enforcing they can't).
 
Static means "reference does not exist outside this object file" (at least in C, not C++, but it can mean that in C++)
The question was why the author wants to make all functions static. If it was preferrable, all functions would be static by default (plus another keyword for non-static).
 
There are people who can't look at their code without getting mad if functions are not sorted alphabetically (for binary search).
This is actually a requirement of style(9). It does make finding a function a little easier. Quoting style(9):

All functions are prototyped somewhere.

Function prototypes for private functions (i.e., functions not used
elsewhere) go at the top of the first source module. Functions local
to one source module should be declared static.

The downside is the duplication of text with the slight risk of not keeping them in sync. For projects outside the FreeBSD source tree, I prefer not to follow this. Note that style(9) does mandate static where appropriate.

Some other reasons for encouraging static are:
  • fewer symbols for the linker
  • less risk of symbol collisions
  • hiding implementation details prevents API abuse where some code elsewhere starts using the internal bits - making future re-design/refactor harder.
 
Don't more current version of C standard let you get form 3 by doing:
I've not seen nested functions in C. Apparently GCC allows this but it is not in the standard. No idea if clang allows this. Apparently this feature carries some elevated risks of stack and overflow attacks.
 
My style has always been to explicitly do function prototypes at the top (or in header file if they are exportable)

Then alphabetize the functions, separating them with
//----------------------------------------------------------------------------------
lines

and finally

// =======================================================
// =======================================================
// =======================================================
int main (int argc, char* argv[]) {
return 0;
}

This gives me good visual queus to know where functions break, and always
expect main() at the bottom

I'm also a huge advocate of requiring real and useful doxygen markups, but
equally annoying is a program that is more comments than it is real code.
 
I've not seen nested functions in C. Apparently GCC allows this but it is not in the standard. No idea if clang allows this. Apparently this feature carries some elevated risks of stack and overflow attacks.
Even in C++ I try to avoid nested functions. Object declarations...sure, but nested functions are often better implemented as lambdas in C++.
 
1. I use forward declarations (because: why not and inlining).
2. I create functions but do not place them in alphabetical order (because: ctags)
3. I use a lot of ("why"/"logic") comments (because: very few people use my programs and why not.)
4. I use doxygen headers but have started going away from them (because: I don't use doxygen much if at all and, LSP; vim + clang)
5. I use code folds (because: why not).
6. Nested inline functions (nope, not std). Seperated/external inline (sure).
 
There are header files for a reason ...
Clearly not, header is public API, private function is in C code with static statement.

A good implementation is the forth of #1 :oops:

static void func1(void);
static void func2(void);

int main(void){}

static void func1(void){}
static void func2(void{}

Strict prototyping style : when your declarations are ok the order of implementation doesn't matter, you can easily move code in source file and move function public <-> private.
 
Last edited:
This discussion opens up another related topic: function size and sourcefile size.

If you need to scroll to see a complete function then it is too long. Keep an idea as viewable on a single page. Functions can always be broken down and/or inlined. If you've got an if/else construct that is longer than three choices then review it as a candidate for indexed jumps (either thru case/switch or via indexed function pointers)

in my 40+ years of programming I don't see the need for huge sourcefiles. most should always be under 300 lines, and virtually NEVER move than 500. (including whitespace breaks)

Conserve vertical screen real-estate.
Do if (x) {fn()}
instead of
if (x)
{
fn()
}

and below is good because it doesn't waste curly brace lines
if (x) {
fn1()
} else {
fn2()
}
 
in my 40+ years of programming I don't see the need for huge sourcefiles. most should always be under 300 lines, and virtually NEVER move than 500. (including whitespace breaks)
Using multiple source files doesn't bloat the final binary. Compilation time is reduced with partial compilation of only modified source files. I'll never understand such big files like this one: FreeRTOS/FreeRTOS-Kernel/tasks.c 8871 lines of code, 427 occurrences for '#if', human cognitive load 1000% o_O
 
If you need to scroll to see a complete function then it is too long. Keep an idea as viewable on a single page. Functions can always be broken down and/or inlined.
Nice theory. Sometimes even works.

Sometimes doesn't work, and sometimes fails spectacularly. In my previous job, we had a 6000 line function. It was perfectly readable, mostly bug free (I remember funding one bug in it, not counting typos and superficial things), and the simplest way to structure the task. We seriously thought about breaking it up into two dozen functions, but then we decided that (a) the individual functions would no longer make sense, as they don't solve an identifiable and describable problem, but instead could only understood in the context of the larger function; (b) the big function carried a significant amount of internal state in it as local variables (all of which was actually needed, we audited that), and that meant that the smaller functions would all have very complex calls, with a dozen parameters; (c) the smaller functions having to change state of the larger function would have been hard to handle, either by using reference or pointer parameters, or packaging changes in complex (object) return types.

Admittedly, that is the extreme example, and I've never seen anything this complicated. But that function handled an extremely complicated work flow (which involved humans, hardware and software interacting).

Sure, we could have completely recoded it, for example into a state machine, with a pool of state at the center, and a variety of control flows (perhaps interlocked threads) handling the interactions. But orchestrating all these participants would have been hell, and bug prone.

If you've got an if/else construct that is longer than three choices then review it as a candidate for indexed jumps (either thru case/switch or via indexed function pointers)
Sometimes that works, and sometimes it is even idiomatic. Sometimes it is crazy, makes the code flow unreadable, and hides control in places where people will least expect it.

In your post, you are focused on code structures, such as long functions and if/else/while statement. I think what is actually more important is to look at data and state. What variables are in scope? Are there every any variables that are in scope (can be read or written) but their content is either undefined (for reading) or irrelevant (for writing)? Which pieces of code look at variables, and which pieces of code change them? And by this I don't so much mean local variables (the loop index "i" is not very important or dangerous), but variables that have longer lifetimes, that are kept in complex data structures or even globally.

Since the beginning of my career (nearly 50 years ago) I have been a proponent of Jackson Structured Programming, which had its bloom in the 70s in COBOL practice. If you know what data a program has in memory, and what that data means, and where it comes from and where it goes, you have won 90% of the war. The rest is then coding rather simple instruction flow.

Conserve vertical screen real-estate.
Do if (x) {fn()}
This one I completely agree with. Having as much code AS IS REASONABLE on the screen at once helps the brain see a bigger part of the picture, which helps it analyze the often complex job the code is doing. The issue here is "as is reasonable". I agree that wasting lines just on braces is bad. My favorite C style (which nobody else likes, but I can live with that):
C:
if (foo)
    do_one();
else {
    do_two_a();
    do_two_b(); }
No curly braces needed for single line blocks, and put the necessary curly braces on the same line as the code they are grouping. This only works if you have a good linter that checks indentation, and you treat every indentation warning as an error that needs to be fixed immediately, before even attempting to compile.

On the other hand: If a piece of code consists of three blocks, each of which is 5 or 6 lines long, and they have identifiable different purposes, then "wasting space" on a blank line is a good idea, because it helps the brain see the structure right away. Similarly, a complex 30 lines of code deserves a 5-line comment at the top explaining what it does and why and how, even if that makes it not fit on a single screen any longer.

With modern monitors, a lot of these concerns are just not relevant any longer. Restricting the width of code to 80 characters is silly, 100 fits on most good computers (even laptops, with two source code windows side by side).

One thing I wonder about is why we haven't started using proportional fonts for coding. Clearly we need fixed-width spaces to get the indentation at the beginning of the line right, but after that, proportionally spaced characters are easier to read and more compact.
 
I shall continue to put curly braces on single lines - and you can't stop me! :p

On a serious note, I got into that habit because it made visually matching braces easier. Today, most editors will tell you which pairs of braces match, making this unnecessary.
 
I shall continue to put curly braces on single lines - and you can't stop me! :p

On a serious note, I got into that habit because it made visually matching braces easier. Today, most editors will tell you which pairs of braces match, making this unnecessary.
With respect to ralphbsz , I have adopted overuse of curly braces too, as it is a rule in autosar/misra safe coding practices...and yeah, visually queuing things while reading code is important.
 
ralphbsz : yeah, I understand, and I expect exceptions without getting bent out of shape about them. I don't think coding style is worth a jihad with others. I just know that with my neuro-divergent brain, certain constructs speak to me, while others just scream chaos...and being a more embedded/applied-science engineer, my domain better allows for shorter functional blocks that are not usually aglorithmically or large state-machine complex.
 
I shall continue to put curly braces on single lines - and you can't stop me! :p
If it helps you ... and more importantly, if it helps the other people who are working with the code you write (and will do so in the future, including your self 10 years from now), then more power to you!

On the other hand, if you are doing it under duress because the "elders" at your company are having a "jihad" (as Kent correctly points out) and using coding rules as a tool for pressure and oppression, then I fee sorry for you. And if you're only doing it out of tradition, you should maybe reflect, try some other options, and see whether you want to change. To quote either Gustav Mahler or Karl Boehm: "Tradition ist Schlamperei" (tradition is sloppiness).

And to Kent's other point: Different domains, different expectations of collaboration and code quality, and different ways of thinking all call for different coding styles. I'm myself finding that as I age, and my brain shrivels up, I enjoy long and internally complex pieces of code less. But certain problems are complex, and trying to break them down into small pieces only obscures the complexity.

Problem du jour (now solved): A microcontroller has two multi-color LEDs attached, which in normal operation show the state of the system by a steady color. It also has three pushbuttons, red green and blue, which can change the state. For safety, you have to press the button for a whole second before any action is performed. To make things more complex, the three buttons have to control four functions, so pressing red and green at the same time is a fourth action. To give the user feedback that they have pressed the button and that an action will happen soon, the LEDs both change once you press a button: The moment you touch a button, they change from their steady state to a fast blinking, in the color that matches the button (like blue-off). If you press the combination button, the blinking is red-green. After one second, to confirm to the user that the button press has done its function, the LEDs change from a fast blinking to a smooth wave (for example blue going smoothly dim-bright in a 2 Hz cycle). The actions can take anywhere between zero and about 20 seconds, so the smooth wave will be present for at least 5 seconds as a visual confirmation. As soon as the action has completed, the LEDs go back to their steady glow (probably in a different color than before, since red/green have the meaning off, auto and disengage). Except for the blue button, which causes the LEDs to continue doing their smooth wave until everything turns off (the blue button means shutdown and cuts power, eventually).

Turns out this is actually a nastily complex piece of code to write. In particular if you consider error and unusual conditions. What should happen if the user presses a button for 0.2 seconds only (answer: it blinks fast while pressed, then goes back to steady, and the button is otherwise ignored). What should happen if the button remains pushed for a long time (answer: the action is performed, and then the smooth wave keeps on until both the action is completed, and the button is released). What if the user pressed an invalid combination (answer: haven't figured out a good answer yet, for now the LEDs just turn off until the buttons are released, and both button presses are ignored). Too bad the system is too small (just a few inches) to have a text display. And too bad an audio indicator wouldn't work (the control box is right next to large pumps and a substantial gas engine). So for now this crappy user interface is the best I can do.

This code is working now (507 lines of Python); I'll write tests for it (both with mocked hardware, and on a lab bench test system with buttons), deploy it into prod, wait for a month or two of real-world experience, and then add sending e-mail notifications of bizarre things. Like wrong button combinations, or a button remaining pressed for an hour, which probably means someone leaned a shovel against the button on the control panel.
 
Back
Top