C Notes
Complete guide to creating custom header files in C — what goes in a header, include guards, separating declaration from definition, multi-file projects, best practices, practical examples.
As your C programs grow beyond a single file, you need a way to share declarations (function prototypes, type definitions, constants) between files. That's what header files are for. They're the interface — the "public API" — of your code modules.
What is a Header File?
A header file (.h) contains declarations that are shared between multiple source files (.c). It tells other files "what exists" without revealing "how it works."
| math_utils.h | math_utils.c | |
|---|---|---|
| (WHAT - interface) | (HOW - implementation) | |
| double add(double, | double add(double a, | |
| double); | double b) | |
| double sqrt(double); | { return a + b; } | |
| #define PI 3.14159 | ... |
What Goes in a Header File
DO put in headers:
// Function prototypes (declarations)
int calculate_sum(int arr[], int n);
char *str_reverse(const char *str);
// Type definitions
typedef struct {
double x, y;
} Point;
// Struct declarations
struct LinkedList;
// Macros and constants
#define MAX_BUFFER_SIZE 1024
#define VERSION "2.0.1"
// Enum definitions
enum Color { RED, GREEN, BLUE };
// External variable declarations
extern int global_count;
// Inline functions (small, frequently called)
static inline int max(int a, int b) { return a > b ? a : b; }DON'T put in headers:
Creating Your First Header
Step 1: The Header File
// calculator.h
#ifndef CALCULATOR_H
#define CALCULATOR_H
// Function declarations
double calc_add(double a, double b);
double calc_subtract(double a, double b);
double calc_multiply(double a, double b);
double calc_divide(double a, double b);
double calc_power(double base, int exponent);
// Constants
#define CALC_VERSION "1.0"
#define CALC_PI 3.14159265358979
#endif // CALCULATOR_HStep 2: The Implementation File
Step 3: Using the Header
// main.c
#include <stdio.h>
#include "calculator.h"
int main() {
printf("Calculator v%s\n\n", CALC_VERSION);
double a = 10.0, b = 3.0;
printf("%.1f + %.1f = %.1f\n", a, b, calc_add(a, b));
printf("%.1f - %.1f = %.1f\n", a, b, calc_subtract(a, b));
printf("%.1f * %.1f = %.1f\n", a, b, calc_multiply(a, b));
printf("%.1f / %.1f = %.4f\n", a, b, calc_divide(a, b));
printf("%.1f ^ %d = %.1f\n", 2.0, 10, calc_power(2.0, 10));
return 0;
}Compile: gcc main.c calculator.c -o calculator
Calculator v1.0 10.0 + 3.0 = 13.0 10.0 - 3.0 = 7.0 10.0 * 3.0 = 30.0 10.0 / 3.0 = 3.3333 2.0 ^ 10 = 1024.0
Include Guards in Detail
// Without guards — PROBLEM:
// main.c includes "a.h" and "b.h"
// b.h also includes "a.h"
// → a.h content appears TWICE → redefinition errors!
// With guards — SOLUTION:
#ifndef MY_HEADER_H // First time: not defined → proceed
#define MY_HEADER_H // Now it's defined
struct Point { int x, y; }; // Included once
#endif // End of guard
// Second inclusion: MY_HEADER_H already defined → entire block skippedGuard Naming Conventions
// Use unique, project-scoped names:
#ifndef PROJECTNAME_MODULE_FILENAME_H
#define PROJECTNAME_MODULE_FILENAME_H
// Examples:
#ifndef MYAPP_NETWORK_HTTP_H
#ifndef GAME_ENGINE_PHYSICS_H
#ifndef STUDENT_DB_RECORDS_HMulti-File Project Structure
Compile with include path: gcc -Iinclude src/*.c -o program
Complete Multi-Module Example
Students (sorted by GPA): [4] Diana GPA: 3.95 [2] Bob GPA: 3.92 [1] Alice GPA: 3.85 [3] Charlie GPA: 3.67
Header File Best Practices
| Practice | Why |
|---|---|
| Always use include guards | Prevents redefinition errors |
| Include only what you need | Reduces compilation time |
| Put the header's own #include at top of .c | Catches missing dependencies |
| Keep headers minimal | Less to recompile when changed |
| Use forward declarations | Reduce include chains |
Never put static functions in headers | Each .c gets a separate copy |
| Document the public API | Headers are your module's interface |
Opaque Pointers — Information Hiding
// database.h — public interface
#ifndef DATABASE_H
#define DATABASE_H
// Opaque type — users can't see inside
typedef struct Database Database;
Database *db_open(const char *filename);
void db_close(Database *db);
int db_insert(Database *db, const char *key, const char *value);
const char *db_get(Database *db, const char *key);
#endifUsers of database.h can only use Database * pointers — they can't access internal fields.
Interview Questions
Q1: What is the difference between a header file and a source file?
A header (.h) contains declarations (what exists — prototypes, types, macros). A source file (.c) contains definitions (how it works — function bodies, variable values). Headers are shared via #include; source files are compiled separately and linked together.
Q2: Why should you never define functions in header files?
If a header defines a function and is included by two .c files, both will contain the function's compiled code. The linker will report "multiple definition" errors. Only declare functions in headers; define them in exactly one .c file.
Q3: What is an opaque pointer and why is it useful?
An opaque pointer is a pointer to a struct whose definition is hidden from the user (only declared in the header, defined in the .c file). Users can pass the pointer around but can't access internal fields. This provides encapsulation — you can change the struct's internals without affecting users.
Q4: What happens if you forget include guards?
If a header is included more than once (directly or indirectly), its contents are duplicated. This causes "redefinition" compilation errors for types, structs, and enums. Without guards, complex projects with many includes become impossible to compile.
Summary
- Header files declare the public interface of a module
- Source files provide the implementation
- Always use include guards (
#ifndef/#define/#endif) - Put only declarations, macros, and type definitions in headers
- Never define functions or non-extern variables in headers
- Use opaque pointers for information hiding (encapsulation)
- Organize projects with separate include/ and src/ directories
- Compile with
gcc -I<include_path> src/*.c -o program
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Header Files in C – Creating Custom Headers.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this C Programming topic.
Search Terms
c-programming, c programming, programming, preprocessor, header, files, header files in c – creating custom headers
Related C Programming Topics