General Schema of a Header File in C

In previous lessons we addressed the study of different aspects of header files in C language:

  1. We studied their purpose and usage through the use of the #include directive;
  2. We saw their use to share types, constants, global variables and functions among multiple source files;
  3. We studied the use of nested inclusion and we addressed the problem of multiple inclusion.

Based on this knowledge we will now see a general schema of a header file in C language that can be used in most cases.

General structure of a header file

When you want to create a header file in C language, it is important to follow a well-defined structure to facilitate its reading and maintenance.

We can divide a header file into different parts, each of which contains a specific type of declarations:

  1. Include guards: are preprocessor directives that serve to avoid multiple inclusion of the same header file.

    These directives are composed of a code block that defines a macro and two #ifndef and #define directives that check if the macro has already been defined. If the macro has not been defined, the code block is executed and the macro is defined. If the macro is already defined, the code block is not executed. Finally, at the end of the header file, the macro is defined with the #endif directive.

    #ifndef FILE_NAME_H
    #define FILE_NAME_H
    
    /* Declarations */
    
    #endif
    
  2. Inclusions: are the preprocessor directives that include the libraries necessary for the header file.

    A header file could, in turn, need to include other header files or system libraries. These inclusions always go before other declarations.

    #include <system_file_1.h>
    #include <system_file_2.h>
    #include "local_file_1.h"
    #include "local_file_2.h"
    
  3. Constant declarations: are the declarations of constants, such as numeric constants and string constants.

    Constants are defined with the #define directive and are used to define values that do not change during program execution.

    #define CONSTANT_1 10
    #define CONSTANT_2 "constant"
    
  4. Type declarations: are the declarations of custom types, such as structures, enumerations and user-defined types.

    These declarations are used to define new data types that can be used throughout the program.

    typedef struct {
        int field_1;
        char field_2;
    } StructureType;
    
    typedef enum {
        Value_1,
        Value_2,
        Value_3
    } EnumerationType;
    
  5. Variable declarations: are the declarations of global variables.

    Global variables are declared with the extern keyword to indicate that they are defined in another source file.

    extern int global_variable_1;
    extern char global_variable_2;
    
  6. Function declarations: are the declarations of functions.

    Functions are declared with their prototype, that is with their name, the types of parameters and the return type.

    void function_1(int argument_1, char argument_2);
    void function_2(int argument_1, char argument_2);
    

Obviously, all these parts are optional. However, regarding include guards, it is always advisable to include them to avoid multiple inclusion of the header file.

Below is a general schema of a header file in C language:

/* Part 1: Include Guards */
#ifndef FILE_NAME_H
#define FILE_NAME_H

/* Part 2: Inclusions */
#include <system_file_1.h>
#include <system_file_2.h>
#include "local_file_1.h"
#include "local_file_2.h"

/* Part 3: Type Declarations */
typedef struct {
    int field_1;
    char field_2;
} StructureType;

typedef enum {
    Value_1,
    Value_2,
    Value_3
} EnumerationType;

/* Part 4: Constant Declarations */
#define CONSTANT_1 10
#define CONSTANT_2 "constant"

/* Part 5: Variable Declarations */
extern int global_variable_1;
extern char global_variable_2;

/* Part 6: Function Declarations */
void function_1(int argument_1, char argument_2);
void function_2(int argument_1, char argument_2);

/* Part 7: End Include Guards */
#endif

This header file schema is good in most cases, but may vary depending on project needs. For example, if the header file is very large, it might be useful to divide it into multiple header files to facilitate its maintenance.