cstructextern

Declaring extern structures in header file in C


I found some code that made me a bit intrigued: there is a header file declaring only structures that are being used in code for an embedded system written in C.

This is an example of code in the header:

#ifdef _structures_h_
 
extern struct{
    uint16_t  State_ON;
    uint16_t  State_OFF;
    uint16_t  State_HI;    
    uint16_t  Counter;
}Switch;

#endif


#ifndef _structures_h_
#define _structures_h_

struct{
    uint16_t  State_ON;
    uint16_t  State_OFF;
    uint16_t  State_HI;    
    uint16_t  Counter;
}Switch;

#endif

In the source files that make use of those structures, in some of them, the inclusion is done like this:

#define _structures_h_
#include "structures.h"

In others there is just the inclusion of the header file as shown below:

#include "structures.h"

This is the first time that I have found such a situation and I was not able to find a reasonable explanation for using this.


Solution

  • The difference between the two code sections is that, in the first (the one that has the extern keyword), the structure variable is declared but not defined. That is, the code specifies that the Switch variable is defined elsewhere (in another translation unit). However, in the second section, the Switch variable is formally declared and defined – or, at least, tentatively defined; that tentative definition will become an actual definition if/when the compilation of the translation unit completes with no other full definition (i.e., one with an initializer) occurring.

    In that second conditional block, the definition of the _structures_h_ token is very strange; and, unless there is more to the header than you have shown, I can really see no purpose for it:

    1. If the header is included multiple times by a single TU, then, with or without that definition, there will be a case of redefinition of the Switch variable with different types (even though those types have exactly equivalent definitions, they are still separate types); that redefinition is an error.
    2. If the header is included by multiple TUs, then the definition of the macro in one will not "carry through" to any others, so you will still (potentially) have multiple instances of the Switch variable from each of the TUs that include the header without first themselves defining the _structures_h_ token.

    But I would agree with you and the other answerer that code like this is both confusing and error-prone: For example, using that header in multiple translation units without a preceding _structures_h_ definition will cause multiple definitions of the Switch variable.

    Also, rather than using the #ifndef ... block, a simple #else would suffice and make things clearer (IMHO).

    Here's a shortened, annotated version of the header:

    #ifdef _structures_h_
    extern struct {
        uint16_t  State_ON;
        uint16_t  State_OFF;
        uint16_t  State_HI;
        uint16_t  Counter;
    } Switch; // Already defined elsewhere - just need the declaration
    #else
    struct {
        uint16_t  State_ON;
        uint16_t  State_OFF;
        uint16_t  State_HI;
        uint16_t  Counter;
    } Switch; // Not yet defined, so we provide a "tentative" definition
    #endif
    

    Or, an even shorter way, which avoids the multiple specifications of the structure (and the errors that could lead to), is to define a separate 'linkage' macro that is either extern or nothing:

    #ifdef _structures_h_
    #define SW_LINKAGE extern
    #else
    #define SW_LINKAGE /*  */
    #endif
    SW_LINKAGE struct {
        uint16_t  State_ON;
        uint16_t  State_OFF;
        uint16_t  State_HI;
        uint16_t  Counter;
    } Switch;