Declaring extern structures in header file in C

139 Views Asked by At

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"
  • My doubt is, is there some reason for declaring the structures in both ways in the same header file?

  • Is there any difference or relevant impact in coding declaring the same struct as extern or not in a header filer?

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.

2

There are 2 best solutions below

7
Adrian Mole On BEST ANSWER

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;
0
Jabberwocky On

This construction basically boils down to this:

header.h

#ifdef _structures_h_

extern struct {
  int  State_ON;
  int  State_OFF;
} Switch;

#endif

#ifndef _structures_h_
#define _structures_h_

struct {
  int  State_ON;
  int  State_OFF;
} Switch;

#endif

file1.c

#include "header.h"

void Function();

int main(void)
{
    Switch.State_ON = 2;
    Function();
}

file2.c

#include <stdio.h>

#define _structures_h_
#include "header.h"

void Function()
{
    printf("Switch.State_ON = %d\n", Switch.State_ON);
}

This is pretty ugly because:

  • in header.h the struct is repeated which is error prone
  • it is unclear when and why we need to have #define _structures_h_ before including header.h
  • the whole thing is confusing as you have experienced yourself.

The usual way this is done looks like this:

header.h

#ifndef _structures_h_
#define _structures_h_

typedef struct {
  int  State_ON;
  int  State_OFF;
} SwitchStruct;

extern SwitchStruct Switch;   // declare the Switch variable so that anybody
                              // who includes header.h can use it
#endif

file1.c

#include "header.h"

SwitchStruct Switch;  // the one and only place where the variable
                      // Switch is defined
void Function();

int main(void)
{
    Switch.State_ON = 2;
    Function();
}

file2.c

#include <stdio.h>
#include "header.h"

void Function()
{
 // we can use Switch because it is declared in header.h
    printf("Switch.State_ON = %d\n", Switch.State_ON);
}