C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
5: Understanding Header Files
Up until now, we've been keeping everything in one file. That works for small scripts, but in a real-world project, your source files will eventually grow into monsters that are impossible to navigate. The solution is to split your code: put the implementation (the how) in one file and the interface (the what) in a header file.
Let's build a simple temperature conversion utility. I want a function that converts Celsius to Fahrenheit, but I want it to live in its own module so I can reuse it across different parts of a larger program without copying and pasting code.
Moving the math into its own file
First, I'll create a file called temp_conv.c. This is where the actual heavy lifting happens. I'm not going to put a main() function here because this file is intended to be a library, not a standalone program.
double celsius_to_fahrenheit(double celsius) {
return (celsius * 9.0 / 5.0) + 32.0;
}
Now, I need a way to tell my main program that this function exists. If I just try to call it from another file, the compiler will have no idea what celsius_to_fahrenheit is when it's processing that specific file. This is where the header file comes in.
Creating the public contract
I'll create temp_conv.h. Think of a header file as a contract. It tells any file that includes it: "I promise that there is a function with this name, these arguments, and this return type available somewhere in the project."
double celsius_to_fahrenheit(double celsius);
Now I can create my main.c and use the function:
#include <stdio.h>
#include "temp_conv.h"
int main() {
double temp = 25.0;
printf("%.2f C is %.2f F\n", temp, celsius_to_fahrenheit(temp));
return 0;
}
Fixing the "implicit declaration" headache
Here is where I usually trip up when I'm rushing. I just tried to compile this using gcc main.c temp_conv.c -o conv, but I realized I had forgotten to actually add the #include "temp_conv.h" line in main.c.
The compiler didn't just stop; it gave me a warning about an "implicit declaration of function." In older versions of C, the compiler would just guess that the function returns an int. Since my function actually returns a double, the program would have likely crashed or returned gibberish. Always pay attention to those warnings—they are the compiler trying to tell you that your "contract" is missing.
Protecting against double-inclusion
There's one more thing. As your project grows, you'll end up with headers including other headers. If header_a.h includes header_b.h, and your main.c includes both, the compiler sees the contents of header_b.h twice. This leads to "redefinition" errors that can be a nightmare to debug.
To fix this, we use "Include Guards." I'm going to wrap my temp_conv.h file in a preprocessor check. It's a bit of boilerplate, but it's non-negotiable in professional C code.
#ifndef TEMP_CONV_H
#define TEMP_CONV_H
double celsius_to_fahrenheit(double celsius);
#endif
Basically, this says: "If TEMP_CONV_H isn't defined yet, define it and read the rest of this file. If it is already defined, skip everything until the end." This ensures that no matter how many times the header is included, the compiler only sees the declarations once.
📋 Practical Task
Building a Rectangle Area Module
Your task is to create a small modular program that calculates the area of a rectangle. You must split the code across three files to practice the workflow we just covered.
- rect_math.h: Create a header file with an include guard. It should contain the function prototype for
double calculate_area(double width, double height). - rect_math.c: Implement the
calculate_areafunction here. - main.c: Include your header file and write a
main()function that asks the user for a width and height, calls the area function, and prints the result.
Compile your project using: gcc main.c rect_math.c -o rect_calc
There are no comments for now.