3

I am wondering why function prototypes are required by MISRA:2012. In the example below, the two prototypes aren't really necessary.

#include <stdio.h>
#include <stdlib.h>

// >>> Truly useless in my opinion
void display(void);
int main(void);
// <<<

void display(void) {
    printf("Hello World!\n");
}

int main() {
    display();
    return EXIT_SUCCESS;
}

The rationale I can read on SO such as here isn't very clear to me. For instance, if main tries to access display before it is declared, the compiler or the static analyzer will raise an error: function display used before declaration.

In other words is it a good idea to create a deviation for this MISRA rule?

nowox
  • 25,978
  • 39
  • 143
  • 293
  • 2
    I think: 1) it means you can order your functions without breaking the code, 2) it oviates any throwbacks to implicit declarations. – Bathsheba Oct 05 '17 at 08:00
  • 1
    IMHO, this is completely moot with a modern compiler warning you about implicit function declarations. But there you have the motivation: With a "bad" compiler or the wrong compiler settings, you could introduce unnoticed bugs by reordering your functions. Always having the prototypes prevents this. –  Oct 05 '17 at 08:06
  • 1
    My advice: enable all the warnings -Wall, -Wextra. Now you can forget half of rules from static analysis tools. – Jean-François Fabre Oct 05 '17 at 08:08
  • That code is bad anyway, since `display()` isn't declared `static` which it of course should be if the intent is that the function is local to the file it's in. – unwind Oct 05 '17 at 09:00
  • @Bathsheba I agree with you, but this is not the rationale given in [Lundin](https://stackoverflow.com/users/584518/lundin) in [this](https://stackoverflow.com/a/46566970/2612235) answer, so I would like to understand more this rule. – nowox Oct 05 '17 at 09:06
  • @FelixPalmen you can always enable warnings that make a bad compiler better. Or if you can't, you can always use a static analyzer. – nowox Oct 05 '17 at 09:07
  • @unwind In this very example, yes `display()` should be static, but we can imagine it is used somewhere else too. – nowox Oct 05 '17 at 09:08
  • @nowox You need to think about what happens to the arguments passed to a function call when there's no prototype... – Andrew Henle Oct 05 '17 at 09:09
  • @AndrewHenle This is exactly the kind of question I don't understand. To me the function itself acts as the prototype. A function defined as `int32_t foo(int32_t bar) { /*...*/ }` is thus well defined. If I try to call this function from another file, I need of course a `extern int32_t foo(int32_t bar);` declared in a header NOT included in the file where `foo` is declared. If `foo` is only used in the same translation unit, it has to be declared before it is used. – nowox Oct 05 '17 at 09:39
  • 1
    @nowox *the function itself acts as the prototype.* Until the code is reordered and it doesn't. *If foo is only used in the same translation unit, it has to be declared before it is used.* That's not true. Not all C compilers require a prototype for a function to be called. Legacy C code might not even compile under newer standards. Again: Think about what happens to arguments passed to a function that doesn't have a prototype. – Andrew Henle Oct 05 '17 at 09:55
  • Practically, a C99 compiler and/or a static analyser will find all bugs caused by missing function prototypes. Which is another reason why you shouldn't deviate from the rule. – Lundin Oct 05 '17 at 13:03
  • The implementation doesn't declare `main()`; it would be unusual for you to do so. Every other function should have a prototype in scope before it is used. There are grounds for having a prototype in scope before it is declared unless it is a static function, in which case its existence with a full prototype (no empty parentheses just because the function takes no parameters — this is C, not C++) gives a prototype. In this case, the function must be defined before it is used. The MISRA rule is sensible (unless it stipulates a declaration for `main()`); you should follow it rigidly. – Jonathan Leffler Oct 05 '17 at 14:40

2 Answers2

3

void display(void); is a function forward declaration. It has prototype format.

As indicated in the link posted, a function prototype is a function declaration with the types of all parameters specified. If there are no parameters, then the parameter list must be (void) (no parameters) and not () (any parameter).

The exact rule 8.2 says:

Rule 8.2 Function types shall be in prototype form with named parameters

The rationale provided (read it, it is pretty good) mentions that this is to avoid old K&R and C90 programs where not all parameters are specified. C99 still allows this at some extent, as long as the parameter types in the function declaration don't collide with the parameter types in the function definition.

Essentially, the rule seeks to ban these kind of functions:

void func1 (x)  // K&R style
int x;
{}

void func2(x)  // sloppy style
{}

All parameters (if any) must have types and names specified.

However, I find nothing in MISRA-C that requires you to write a function declaration for each function. This means that your example code would conform to this MISRA rule with or without the function declaration.


Though as I mentioned in a previous answer, writing .c files without function declarations (in prototype format) is sloppy practice. If your functions need to be called in a certain order, it should be made obvious by the program design, function naming and comments/documentation. Not by the order that they happen to be declared inside the .c file.

There should be no tight coupling between the source code line where a function is declared in the .c file and that function's behavior/use.

Instead, functions should be defined in an order that makes sense logically. A common way to write .c files is to keep all public functions, that have their function declaration up in the .h file, at the top of the .c file. Then let the internal functions (those with static/internal linkage) sit at the bottom. This model requires function declarations of all the internal functions. Another option is to put all internal functions on top, and the public functions at the bottom. As long as you are consistent, either is fine.

What's most important is that if function definitions inside the .c file are re-ordered, it should not break the program or cause compiler errors. The easiest way to ensure this is to always provide function declarations for every single function in your program.

Note that the function declarations on top of the file is not "truly useless" at all, as they provide a quick summary of all functions present in the C file. It is a way to write self-documenting code.


Note that the C standard allows no prototype for main(), as a special case.

Note that in addition, rule 8.7 and 8.8 disallows you to use void display(void) without static, since the function is only used in one translation unit.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • What's interesting is that I don't get neither 8.7 nor 8.8, but 8.4: `[MISRAC2012-Rule-8.4]:Definition of externally-linked `main()' has no compatible declaration.`. However if I add `int main(void);` I can get rid of this error. Do you know why? – nowox Oct 06 '17 at 07:35
  • 1
    @nowox - MISRA published a Technical Corrigendum in June 2017, to explicitly add an exception to Rule 8.4 for main() so suggest you get the update from your tool-vendor. – Andrew Oct 13 '17 at 13:12
2

If you do not declare the function, any function call will call default argument promotions for each argument because it is considered that the function has the semantics of the C89 standard.

Case 1:

Consider a call f(5), where the parameter of the function is of type double. The code of f will treat 5 as double, while the default arith promotions will pass only an integer.

A loss of header file that contains declarations may have your code pass integers and in fact the function to treat them as pointers causing seg faults. Here is a concrete example:

Case 2:

Suppose you want to use the function strtok and you do not include the header string.h with the declaration -- char *strtok(char *str, const char *delim)

Now you make the mistake to consider separator 'A' instead of "A". So if you forget the signature but keep in mind the meaning of parameters, if you do not include the header the code will compile with no warning and of course the code from strtok will consider your char 'A' (that is converted in an integer (=95)) as the actual argument. The code considers it is a pointer to a string and will try to access the pointer from the location 95 finishing with segfault.

Case 3:

Here is another typical example of code that segfaults on some architecture -- even if you do not make any mistake it still segfaults.

char *subtoken;
subtoken = strtok(str, delim, &saveptr);

In this case the function strtok (missing the declaration from string.h) is considered to return an int, so an implicit conversion from int->char* is made. If int is represented on 32 bits and the pointer on 64 bits, clearly the value of subtoken will be errorneous and will produce seg fault.

alinsoar
  • 15,386
  • 4
  • 57
  • 74