The answer is clear, but I don't understand the reasoning. It would seem better to me if a tentative definition were to be treated as a definition for the sake of this rule. Otherwise, this rule permits something like the following:
Code:
/* header.h */
uint16_t var; /* tentative definition */
uint16_t get_value(void);
Code:
/* file1.c */
#include "header.h"
uint16_t var = 3; /* true definition */
Code:
/* file2.c */
#include "header.h"
uint16_t get_value(void) { return var; }
Many of the popular compilers come from a UNIX background where such tentative definitions are emitted as a "common" in the object file, a technique borrowed from Fortran. This extension is typically on by default even in strict language modes. When this happens, the multiple definitions of var that occur in both file1.c and file2.c won't be diagnosed by the user's linker.
While this sort of code violates other MISRA rules like 8.5 and 8.6, I don't see anything to be gained by relying on a system rule to catch something that should be caught within a single translation unit.
It is true that these popular compilers have a switch to turn off common variables (for example, GCC has -fno-common).
But, there's no loss of expressiveness by requiring a declaration that is not a tentative definition. In anything, it also helps to avoid mistakes of the sort:
Code:
const uint8_t foo;
uint8_t func() {
/* It would appear that this function just returns 0, but it actually returns 1. */
return foo;
}
... // many lines of code
const uint8_t foo = 1;
My suggestion would be to add a sentence to the end of the amplification to the effect of:
For the purposes of this rule, a tentative definition is considered a definition.
This might go well with a rule to prohibit tentative definitions for external symbols. (There is a slight use-case for tentative definitions of static symbols, though).
Thanks for your time, and keep up the good work.
Best Regards,
-Greg