Skip to content

C Header Files

A header file in C (typically with a .h extension) contains declarations and macro definitions intended to be shared across multiple source files (.c files). They act as interfaces, defining what functions, variables, types, and macros are available, without necessarily providing their implementation (definitions).

Two main types of header files:

  • System Header Files: Provided by the C compiler and standard library (e.g., <stdio.h>, <stdlib.h>, <string.h>, <math.h>). They declare standard functions, types, and macros.
  • User-Defined Header Files: Created by programmers for their own projects to organize code and share declarations between their .c files (e.g., "my_graphics.h", "network_utils.h").
  • Modularity: Break down large programs into smaller, manageable .c files (modules). Header files define the public interface of each module.
  • Reusability: Declarations written once in a header file can be used in many .c files by simply including the header.
  • Consistency: Ensures all source files use the same declarations for shared functions, types, and constants, preventing errors.
  • Readability: Separates interface (declarations) from implementation (definitions), making code easier to understand.
  • Reduced Compile Times (with proper use): Changes to implementation details in a .c file only require recompiling that file, not all files that use its interface (defined in the header).

Use the #include preprocessor directive to insert the content of a header file into your source file.

Two forms:

Used for system header files. The preprocessor searches for the file in a standard list of system directories (compiler-specific, often configurable).

#include <stdio.h> // Standard Input/Output
#include <math.h> // Mathematical functions

Used for user-defined header files (your own project’s headers). The preprocessor typically searches for the file first in the same directory as the current source file, and then in the standard system directories (or other specified include paths).

#include "my_module.h"
#include "config/settings.h"

Header files should generally contain declarations, not definitions that allocate storage or generate code.

  • Function Prototypes (Declarations): extern int calculate_sum(int a, int b);
  • Structure, Union, and Enum Definitions: struct Point { int x, y; };, typedef enum { RED, GREEN, BLUE } Color;
  • Typedef Definitions: typedef unsigned char BYTE;
  • Macro Definitions: #define MAX_SIZE 100
  • Global Variable Declarations (using extern): extern int global_error_code; (The actual definition int global_error_code = 0; goes in a .c file).

What Should NOT Go in a Header File (Generally):

Section titled “What Should NOT Go in a Header File (Generally):”
  • Function Definitions (Implementations): Placing function bodies in headers leads to multiple definition errors if the header is included in more than one .c file.
  • Global Variable Definitions (without static): Defining int my_global; in a header causes multiple definitions.
  • Large Amounts of Code: Headers should be concise interfaces.

Exception: static inline functions (C99) are often defined in header files as they don’t cause linker errors.

If a header file is included multiple times in the same compilation unit (e.g., indirectly through other headers), its contents will be processed multiple times. This can lead to errors like redefinition of types or macros.

To prevent this, use include guards, a standard preprocessor idiom:

// --- my_module.h ---
#ifndef MY_MODULE_H // Check if MY_MODULE_H is not yet defined
#define MY_MODULE_H // Define MY_MODULE_H to prevent future inclusions
// --- Header content goes here ---
#define VERSION "1.0"
struct MyData {
int id;
char value[50];
};
typedef struct MyData DataItem;
extern void process_data(DataItem *item);
// --- End of header content ---
#endif // MY_MODULE_H

The first time my_module.h is included, MY_MODULE_H is not defined, so the preprocessor defines it and processes the header content. Subsequent inclusions in the same .c file will find MY_MODULE_H already defined, and the preprocessor will skip the content between #ifndef and #endif.

The guard macro name (MY_MODULE_H) should be unique to the header file (often based on the filename).

Some compilers support #pragma once as a simpler alternative, but include guards are more portable.