Header Files and the #include Directive
From the formatted functions in c program curriculum
TL;DR
Header files (.h files) contain declarations of functions and variables, letting you tell the compiler what's available without providing the full implementation. The #include directive brings the contents of these header files into your C source code. This helps organize your code, promotes reusability, and avoids redundant declarations.
1. The Mental Model
Think of header files as a table of contents or an index for your C programs. They list out what functions and variables exist, so your main code knows how to call them, even if the actual function code is in a different file. #include is like saying, "Hey, compiler, look at this table of contents too!"
2. The Core Material
In C programming, you often write functions that you want to use in multiple parts of your program or across different program files. Instead of rewriting the function declarations (the function's signature) every time, you put them into a header file.
A header file typically has a .h extension. It contains:
* Function prototypes: These declare a function's name, return type, and parameters, but not its body.
* Macro definitions: Constants or simple expressions defined with #define.
* Type definitions: typedef for creating aliases for data types.
* Structure and union declarations: Definitions of custom data types.
The #include directive is a preprocessor command. The C preprocessor is the first step in compiling your code. When it sees #include, it essentially copies the entire content of the specified file directly into your source code file at that point.
There are two main ways to use #include:
#include <filename.h>: Used for standard library header files (likestdio.hfor input/output). The preprocessor searches for these files in standard system directories.#include "filename.h": Used for your own custom header files. The preprocessor searches for these files first in the current directory (where your source file is located) and then in standard system directories.
Why use Header Files?

Photo by Cup of Couple on Pexels
- Modularity: You can split your code into logical units. For example, all math-related functions could go into
math_utils.handmath_utils.c. - Reusability: You can easily use functions defined in one file in many other files just by including the header.
- Consistency: Avoids typing errors by having a single source for function declarations. If a function signature changes, you only update it in one place.
- Compilation Speed: While not directly faster for a single compile, it helps larger projects by only recompiling source files that have changed, and linking the object files.
Here's how the process generally works:
graph LR
A["Your Source Code (e.g., main.c)"] --> B["Preprocessor (sees #include)"]
subgraph Header Files
C["Standard Library Header (e.g., stdio.h)"]
D["Your Custom Header (e.g., my_functions.h)"]
end
B --> C
B --> D
C --> E["Combined Source Code"]
D --> E
E --> F["Compiler"]
F --> G["Object File (.o)"]
Example of a Custom Header File

Photo by Zulfugar Karimov on Pexels
Let's say you have a file my_functions.h:
// my_functions.h
#ifndef MY_FUNCTIONS_H
#define MY_FUNCTIONS_H
// Function prototype for adding two integers
int add(int a, int b);
// Function prototype for multiplying two integers
int multiply(int a, int b);
#endif // MY_FUNCTIONS_H
And its corresponding implementation file my_functions.c:
// my_functions.c
#include "my_functions.h" // Include our own header
// Function definition for add
int add(int a, int b) {
return a + b;
}
// Function definition for multiply
int multiply(int a, int b) {
return a * b;
}
Now, in your main.c file, you can use these functions:
// main.c
#include <stdio.h> // For printf
#include "my_functions.h" // For add and multiply
int main() {
int sum = add(5, 3);
int product = multiply(5, 3);
printf("Sum: %d\n", sum); // Output: Sum: 8
printf("Product: %d\n", product); // Output: Product: 15
return 0;
}
Notice the #ifndef, #define, and #endif lines in my_functions.h. These are called include guards. They prevent the contents of the header file from being included multiple times in a single compilation unit, which would lead to errors (like "redefinition of 'add'").
3. Worked Example
Let's create a simple set of files to manage basic calculations for a circle.
File 1: circle_math.h
// circle_math.h
#ifndef CIRCLE_MATH_H
#define CIRCLE_MATH_H
#define PI 3.14159f // Define PI as a float constant
// Function prototype to calculate the area of a circle
float calculate_circle_area(float radius);
// Function prototype to calculate the circumference of a circle
float calculate_circle_circumference(float radius);
#endif // CIRCLE_MATH_H
File 2: circle_math.c
// circle_math.c
#include "circle_math.h" // Include our own header
// Function definition for area
float calculate_circle_area(float radius) {
return PI * radius * radius;
}
// Function definition for circumference
float calculate_circle_circumference(float radius) {
return 2 * PI * radius;
}
File 3: main.c
// main.c
#include <stdio.h> // For printf
#include "circle_math.h" // For PI, calculate_circle_area, calculate_circle_circumference
int main() {
float r = 5.0f;
float area = calculate_circle_area(r);
float circumference = calculate_circle_circumference(r);
printf("Circle with radius %.2f:\n", r);
printf(" Area: %.2f\n", area);
printf(" Circumference: %.2f\n", circumference);
return 0;
}
To compile these, you'd typically do something like:
gcc main.c circle_math.c -o circle_app
Then run: ./circle_app
Output:
Circle with radius 5.00:
Area: 78.54
Circumference: 31.42
Here, main.c doesn't need to know how the area or circumference are calculated, only that the functions exist (thanks to circle_math.h).
4. Key Takeaways
- Header files (
.h) declare functions, macros, and types, providing an interface to your code. #includeis a preprocessor directive that inserts the content of a specified file into your current source file.- Use angle brackets (
< >) for standard library headers and double quotes (" ") for your custom headers. - Header files promote code organization, reusability, and maintainability.
- Include guards (
#ifndef,#define,#endif) prevent multiple inclusions of the same header, avoiding redefinition errors. - Function definitions (the actual code) belong in
.cfiles, while their declarations belong in.hfiles.
Common Mistakes to Avoid:
* Forgetting include guards: This leads to compilation errors when a header is indirectly included multiple times.
* Putting function definitions in header files: Only declarations should go in headers; full function code belongs in .c files.
* Using wrong include syntax: Remember < > for system headers, "" for your own.
* Not including necessary headers: Your .c file needs to #include its own corresponding .h file to ensure consistency and catch declaration/definition mismatches early.
5. Now Try It
Create two files: math_ops.h and math_ops.c. In math_ops.h, declare a function int subtract(int a, int b);. In math_ops.c, define this subtract function. Then, create a main.c file that includes math_ops.h and calls subtract with two numbers, printing the result. Compile and run it.
Success looks like: A working program that prints the result of your subtraction, demonstrating that main.c successfully used the function declared in your custom header and defined in another source file.
Frequently asked about Header Files and the #include Directive
Study this next
Get the full formatted functions in c program curriculum
Clone the complete plan to your dashboard for unlimited AI-generated notes, practice quizzes, and a personalised revision schedule.
Save this course free