问题
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?
回答1:
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.
回答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.
来源:https://stackoverflow.com/questions/46580644/why-function-prototypes-are-they-required-in-misra2012