Skip to Content
Course content

29: Function Prototypes and Header Files

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Up until now, we've been keeping everything in a single file. That works fine for tiny scripts, but in the real world, a single C file with 5,000 lines of code is a nightmare to maintain. To keep things sane, we split our logic into different files. This is where function prototypes and header files come in.

Let's build a small utility for calculating rectangle properties. I want to keep the math logic separate from the user interface in main.c.

The "Wrong Way" to Organize Functions

I'll start by writing a simple program. I'm going to put my logic function below main because I personally find it easier to read the high-level flow of the program first before diving into the implementation details.

#include <stdio.h>

int main() {
    double width = 5.0, height = 10.0;
    double area = calculate_area(width, height);
    printf("Area: %.2f\n", area);
    return 0;
}

double calculate_area(double w, double h) {
    return w * h;
}

If you try to compile this, your compiler is going to complain. Depending on your settings, you'll get a warning or an error about an "implicit declaration of function." The problem is that C reads files from top to bottom. When the compiler hits calculate_area inside main, it hasn't seen the definition yet, so it just guesses what the function looks like. Usually, it guesses wrong, and that leads to bugs that are a pain to track down.

Fixing the Flow with Prototypes

To fix this, I need a function prototype. Think of a prototype as a "spoiler" for the compiler. It tells C: "Hey, there's a function called calculate_area coming later; it takes two doubles and returns a double. Trust me on this."

#include <stdio.h>

// This is the prototype
double calculate_area(double w, double h);

int main() {
    double width = 5.0, height = 10.0;
    double area = calculate_area(width, height);
    printf("Area: %.2f\n", area);
    return 0;
}

double calculate_area(double w, double h) {
    return w * h;
}

Now it compiles perfectly. But we're still in one file. If I add ten more shapes, main.c becomes a mess again.

Moving Logic to a Dedicated Header and Source

The professional way to handle this is to move the implementation to a .c file and the "contract" (the prototype) to a .h file. Let's create rect_math.h and rect_math.c.

First, the header file. I'm adding "include guards" here (the #ifndef part). I've seen too many beginners skip this, only to have their program crash because a header was included twice in a complex project. These guards ensure the compiler only reads the declarations once.

// rect_math.h
#ifndef RECT_MATH_H
#define RECT_MATH_H

double calculate_area(double w, double h);
double calculate_perimeter(double w, double h);

#endif

Now, the implementation file. This is where the actual work happens. Notice I include my own header file here to make sure the definitions match the prototypes.

// rect_math.c
#include "rect_math.h"

double calculate_area(double w, double h) {
    return w * h;
}

double calculate_perimeter(double w, double h) {
    return 2 * (w + h);
}

Finally, my main.c becomes incredibly clean. I just include the header, and I have access to all the math functions without cluttering my primary logic.

// main.c
#include <stdio.h>
#include "rect_math.h"

int main() {
    double w = 5.0, h = 10.0;
    printf("Area: %.2f\n", calculate_area(w, h));
    printf("Perimeter: %.2f\n", calculate_perimeter(w, h));
    return 0;
}

Connecting the Pieces

One last thing: you can't just compile main.c anymore. The compiler knows calculate_area exists because of the header, but it doesn't know where the actual code is. You have to compile both C files together.

If you're using GCC, you'd run something like this:

gcc main.c rect_math.c -o rect_tool

The compiler handles the individual files, and the linker ties them together into one final executable. It's a bit more work upfront, but this is how actual software is built. It allows multiple people to work on different files simultaneously without stepping on each other's toes.




📋 Practical Task

Build a Circle Geometry Library

Your task is to create a small multi-file program that calculates properties of a circle. You must split the code across three specific files to practice the organization we just covered.

  • circle_math.h: Define include guards and provide prototypes for two functions: calculate_circle_area and calculate_circle_circumference. Both should take a double radius as an argument and return a double.
  • circle_math.c: Implement the two functions. Use 3.14159 as the value for PI.
  • main.c: Include the circle_math.h header. Ask the user to input a radius via scanf, then call your library functions to print the area and circumference.

Challenge: Try compiling the program using a single command (e.g., gcc main.c circle_math.c -o circle_tool) and ensure there are no "implicit declaration" warnings.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.