This article is a guide for entry-level embedded systems developers learning more about the C language. One of the basic mechanisms commonly used in C that you should become familiar with when learning C are include guards. Include guards are a mechanism used in C and C++ programming to prevent multiple inclusions of the same header file. They help avoid issues such as redefinition errors, which can occur when the same header file is included multiple times in a program.
Example of Include Guards
#ifndef EXAMPLE_H
#define EXAMPLE_H
// Content of the header file
void myFunction();
#endif // EXAMPLE_H
How it works
Include guards use a combination of a unique macro definition and an #ifndef preproccessor directive. It is common practice to give the macro the same name as the file in which it is used. The #ifndef directive works in the opposite way to the #ifdef directive. If no EXAMPLE_H symbol has been created during preprocessing up to this point, code starting from the next line to the nearest #endif directive will be added. However, we must first define the EXAMPLE_H macro to indicate that the file’s content has been already included.
When use Include Guards
In complex programs it is very common situatin to include a header file multiple times in a program, either directly or indirectly. The compiler may encounter duplicate definitions of variables, functions, classes, or other entities. This causes compilation errors. Include guards ensure that a header file’s contents are only included once per compilation unit. Alternatively, you can use #pragma once as a simpler alternative to include guards. This directive ensures that the file is only included once, but it is not part of the C standard and may not be portable to all compilers.
#pragma once
// Content of the header file
void myFunction();
Another common situation where they are used is to create a simple form of configuration. By defining or not defining certain macros, you can control which parts of a header file are included, effectively customising the behaviour or configuration of a program. This is particularly useful for enabling or disabling certain features, setting defaults, or including platform-specific code.
How Include Guards may work as configuration
Define macros before including the header file, either in the source code as some configuration header file or through compiler flags (e.g., using -D with GCC/Clang). Use #ifndef
(or other preprocessor directives like #ifdef
and #if
) to include or exclude code sections based on the presence or absence of the macros. Let’s demonstrate this with some sample code. We want to do some configuration to specify whether „debug mode” and additional „logging information” should be used. We also have some buffers whose size can be adjusted
#ifndef CONFIG_H
#define CONFIG_H
// Enable or disable debug mode
#ifndef DEBUG
#define DEBUG 0 // Default: debug mode is off
#endif
// Enable logging
#ifndef ENABLE_LOGGING
#define ENABLE_LOGGING 1 // Default: logging is enabled
#endif
// Maximum buffer size (can be overridden by defining BUFFER_SIZE)
#ifndef BUFFER_SIZE
#define BUFFER_SIZE 1024
#endif
#endif // CONFIG_H
You can save this as a file called some_config.h and use it to quickly configure what you want to build. By defining macros at compile time, you can override the default values. This is particularly useful when building different firmware versions on CI piplines
gcc -DDEBUG=1 -DENABLE_LOGGING=0 -DBUFFER_SIZE=2048 main.c -o program
Summary
Include guards are a simple but powerful tool for preventing multiple inclusions of header files in C programs. Using them is not mandatory, but it is good practice. They ensure that your code compiles without redefinition errors and are a cornerstone of good header file design. Another option is to use include guards for configuration, which is a practical and efficient way to manage compile-time settings in a project. However, for larger or more dynamic projects, run-time configuration mechanisms may be more appropriate. If you haven’t used them before, it’s highly recommended that you get started.