MISRA Discussion Forums

Full Version: Rule 8.4 - How are tentative definitions handled
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Which of the following are compliant with rule 8.4?

Code:
extern int32_t  ext_val0 = 3;

       int32_t  ext_val1 = 3

extern int32_t  extval2;              
       int32_t  extval2 = 3;

       int32_t  ext_val3;                
       int32_t  ext_val3 = 3;

       int32_t  ext_val4;
This will depend on the presence of other definitions as to whether the Tentative definition is treated as a declaration or a definition.

Code:
extern int32_t  ext_val0 = 3;   // Non-compliant - definition and no declaration

       int32_t  ext_val1 = 3;   // Non-compliant - definition and no declaration

extern int32_t  extval2;        // Compliant - declaration
       int32_t  extval2 = 3;    // followed by compatible definition

       int32_t  ext_val3;       //  Compliant - Tentative definition, but treated as declaration
       int32_t  ext_val3 = 3;   //  because followed by compatible definition

       int32_t  ext_val4;       // Non-compliant -   Tentative definition,
                                // which becomes a definition, initialising ext_val4 to 0.
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
Thank you for your comments. We will consider it when we next review this rule.