You’ve written some code. It works. But now you want to use that rand() generator or that bubble_sort routine in a completely different project. Copy-pasting is lazy. It’s also brittle. When you update one version, you have to hunt down every copy of the code.
The professional move? Build a library.
In the C ecosystem, a library isn’t a magical black box. It’s just two files wearing different hats. You need a header file (.h ) and a source file (.c ). The header tells the rest of the world what functions exist. The source file actually does the work.
Building the Interface: util.h
The header file acts as the contract. It doesn’t contain the logic. It contains the promises. Constants, types, and function prototypes.
Create a file named util.h.
Those extern keywords are doing heavy lifting. They tell the compiler, “Don’t look for the implementation here. I’ll find it later during linking.” If you’re stuck on a legacy compiler that predates modern C standards, strip the parameters from bubble_sort. Old compilers are picky like that.
Hiding the Machinery: util.c
Now for the actual code. Save this as util.c.
Notice the include directive. We use quotes ("util.h" ) instead of angle brackets ( ). The difference matters. Quotes look in your current directory. Angle brackets look in system directories. This file is local. It belongs here.
Here’s the subtle part: rand_seed. It sits in util.c. It is not in util.h. This is information hiding in action. Code using this library can call rand(), but it can’t touch the seed directly. The state is encapsulated. For strict enforcement, you could slap the static keyword in front of the variable definition. That locks it down tight. No external access. Period.
Using the Library: main.c
Now, write the program that uses it. Save this as main.c.
Shorter? Yes. Cleaner? Absolutely. The main program doesn’t care how rand() generates numbers. It just demands a number. That separation is the entire point.
Compiling and Linking
How do you turn these separate files into a running executable? You follow the three-step dance.
First, compile the library source into an object file. Assuming you’re on a UNIX-like system using GCC:
The -c flag is non-negotiable here. It tells the compiler to stop after producing the object file. It creates util.o. This file contains machine code, but it’s incomplete. It has no main function. It can’t run on its own. It’s a component, not a product.
Next, compile your main program:
This generates main.o. Again, just machine code. Still stuck without the library’s logic.
Finally, link them together.
This command takes both object files and stitches them into a single executable named main. Now you have a complete program. Run it with ./main (or just main if you’re on Windows or have it in your path).
It’s a lot of steps for two functions. But imagine doing this for a project with fifty files. You compile each .c file once. You link them together. Change a function in the library? Recompile only that file. Re-link. The rest stays untouched.
Makefiles automate this























