Tutorials Logic, IN info@tutorialslogic.com

C Header Files, Include Guards, and Linkage

Module Interface

A header describes an interface shared by translation units. It normally contains function declarations, type definitions, constants, and carefully designed macros; executable definitions usually belong in one .c file.

Including a header copies its preprocessed contents into the current source file. Guards prevent repeat inclusion inside one translation unit, but they do not repair multiple externally linked definitions placed in a header.

Header Purpose

A .h file publishes declarations that callers need. A .c file supplies private helpers and the definitions that perform the work. Keeping this boundary narrow reduces rebuilds and lets implementations change without rewriting callers.

Syntax Meaning
#include <stdio.h> System header - searched in compiler's include path
#include "myheader.h" Custom header - searched in current directory first
  • System headers - provided by the C standard library: <stdio.h>, <stdlib.h>, <string.h>, etc.
  • Custom headers - created by you to organize your own code.

Common Standard Library Headers

Header Purpose Key Functions
<stdio.h> Input/Output printf, scanf, fopen, fclose, fprintf
<stdlib.h> General utilities malloc, free, atoi, exit, rand, qsort
<string.h> String operations strlen, strcpy, strcat, strcmp, memcpy
<math.h> Math functions sqrt, pow, sin, cos, floor, ceil, fabs
<time.h> Date and time time, clock, difftime, strftime
<ctype.h> Character classification isalpha, isdigit, toupper, tolower
<errno.h> Error codes errno, perror, strerror
<limits.h> Type limits INT_MAX, INT_MIN, CHAR_MAX, LONG_MAX
<stdbool.h> Boolean type (C99) bool, true, false
<stdint.h> Fixed-width integers int8_t, uint32_t, int64_t

Creating a Custom Header File

The key rule: always use include guards (or #pragma once) to prevent a header from being included multiple times in the same translation unit.

Custom Header - mathutils.h + mathutils.c + main.c

Custom Header - mathutils.h + mathutils.c + main.c
// mathutils.h - declarations only (no implementation)

#ifndef MATHUTILS_H   // include guard: if not already defined...
#define MATHUTILS_H   // ...define it (prevents double inclusion)

// Constants
#define PI 3.14159265358979

// Function prototypes (declarations)
int    add(int a, int b);
int    subtract(int a, int b);
double circleArea(double radius);
int    isPrime(int n);

#endif  // MATHUTILS_H

Module Implementation

Module Implementation
// mathutils.c - implementations
#include "mathutils.h"  // include our own header

int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

double circleArea(double radius) {
    return PI * radius * radius;
}

int isPrime(int n) {
    if (n < 2) return 0;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) return 0;
    }
    return 1;
}

Caller and Build Command

Caller and Build Command
// main.c - uses the mathutils module
#include <stdio.h>
#include "mathutils.h"  // our custom header

int main() {
    printf("add(3, 4)       = %d\n",   add(3, 4));
    printf("subtract(10, 3) = %d\n",   subtract(10, 3));
    printf("circleArea(5.0) = %.2f\n", circleArea(5.0));
    printf("isPrime(17)     = %d\n",   isPrime(17));
    printf("isPrime(18)     = %d\n",   isPrime(18));
    printf("PI              = %.5f\n", PI);
    return 0;
}

// Compile: gcc main.c mathutils.c -o app
// Output:
// add(3, 4)       = 7
// subtract(10, 3) = 7
// circleArea(5.0) = 78.54
// isPrime(17)     = 1
// isPrime(18)     = 0
// PI              = 3.14159

Include Guards vs #pragma once

Method Syntax Portability Notes
Include guards #ifndef / #define / #endif Standard C - works everywhere Verbose but guaranteed
#pragma once #pragma once Supported by GCC, Clang, MSVC Simpler, not in C standard

Declarations and Definitions

A declaration tells the compiler a name and type. A definition allocates an object or supplies a function body. Compatible declarations may appear in several translation units, but an externally linked object or function normally needs exactly one definition in the linked program.

Header-Safe Item Usually Keep in .c
Function prototype Non-inline function body
struct, union, enum, and typedef definitions Private helper definitions
extern object declaration The one external object definition
Preprocessor or enum constant Mutable module state
Small static inline helper Large implementation logic

Linkage Choices

An extern declaration refers to a definition with external linkage elsewhere. A file-scope static name has internal linkage and remains private to that translation unit. A static inline function in a header creates an internal helper for each translation unit and should remain small.

  • Prefer accessor functions over exporting mutable global objects.
  • Forward-declare a struct when callers need only pointers to it.
  • Include a public header first in its implementation file to expose missing dependencies.
  • Document pointer ownership, nullability, units, and buffer capacities beside declarations.

Dependency Hygiene

A public header should compile when included first in an otherwise minimal source file. Include the headers needed by its own declarations instead of relying on an unrelated transitive include, but avoid exposing implementation-only dependencies.

  • Use one responsibility per public header and a guard derived from its project path.
  • Break include cycles with pointer-only forward declarations or a clearer ownership boundary.
  • Rebuild all dependents when a public type, macro, or inline helper changes.
Before you move on

Header Review

5 checks
  • The header compiles first with its own declared dependencies.
  • Its guard macro is unique to the project path.
  • Externally linked definitions live in exactly one source file.
  • Private implementation names use internal linkage.
  • The interface documents ownership and valid ranges.

Header Failures

  • Unknown type name

    Include the defining header inside the header that uses the type.
  • Multiple definition error

    Move the definition to one .c file and leave an extern declaration in the header.
  • Conflicting function types

    Include the public header in the implementation and remove handwritten duplicate prototypes.
  • Cyclic includes

    Use a forward declaration for pointer-only dependencies or redesign ownership.

Try this next

Build a C Module

0 of 2 completed

  1. Create counter.h, counter.c, and main.c with caller-owned state. Keep representation private when callers do not need the fields.
  2. Place a global definition in a header, observe the failure, then repair it with extern. Compile two source files that include the header.
Next Step
Next Practice

Finish the concept here, then reinforce it with hands-on coding, interview prep, or a tool that matches the topic.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.