Creating a Library in C: Complete Guide
Creating a Library in C: A Comprehensive Tutorial
Libraries are collections of reusable functions and code that can be shared across multiple programs. They help organize code into separate modules, making applications easier to develop and maintain. Libraries promote code reuse and improve modularity by separating functionality into independent, manageable components.
This tutorial provides an in-depth exploration of creating libraries in C: static libraries, shared (dynamic) libraries, the compilation process, practical examples, common pitfalls, and best practices.
Conceptual Introduction & Fundamentals
What is a Library?
A library is a collection of pre-compiled object files that can be linked into programs. Libraries contain reusable functions, constants, and data structures that multiple programs can use without recompiling the source code.
Why Create Libraries?
- Code reuse: Write once, use in many programs.
- Modularity: Separate functionality into independent components.
- Maintainability: Update library code without changing all programs.
- Distribution: Share code without revealing source (for static libraries).
- Efficiency: Compile once, link many times.
- Organization: Group related functions together.
Static vs. Shared Libraries
| Aspect | Static Library | Shared Library |
|---|---|---|
| File extension | .a (Unix), .lib (Windows) | .so (Unix), .dll (Windows) |
| Link time | At compile/link time | At runtime |
| Executable size | Larger (code copied in) | Smaller (references library) |
| Updates | Requires recompilation | Library can be updated independently |
| Memory usage | Each program has own copy | Shared among programs |
| Portability | Self-contained | Requires library present at runtime |
Library Components
| File Type | Extension | Description |
|---|---|---|
| Header file | .h | Function declarations, macros, types |
| Source file | .c | Function implementations |
| Object file | .o / .obj | Compiled code, not linked |
| Static library | .a | Archive of object files |
| Shared library | .so | Dynamically linked library |
Syntax & Core Mechanics
Creating a Static Library
Step 1: Create Header File
mylib.h:
c
#ifndef MYLIB_H #define MYLIB_H /* Function declarations */ void hello(void); int add(int a, int b); int multiply(int a, int b); #endif
Step 2: Create Source File
mylib.c:
c
#include <stdio.h> #include "mylib.h" void hello(void) { printf("Hello from library!\n"); } int add(int a, int b) { return a + b; } int multiply(int a, int b) { return a * b; } Step 3: Compile Object Files
bash
gcc -c mylib.c -o mylib.o
Step 4: Create Static Library
bash
ar rcs libmylib.a mylib.o
r: Insert/replace membersc: Create archive if it doesn’t exists: Create index for faster linking
Step 5: Use the Static Library
main.c:
c
#include <stdio.h> #include "mylib.h" int main(void) { hello(); printf("5 + 3 = %d\n", add(5, 3)); printf("5 * 3 = %d\n", multiply(5, 3)); return 0; } Compile:
bash
gcc main.c -L. -lmylib -o myprogram
Run:
bash
./myprogram
Expected output:
text
Hello from library! 5 + 3 = 8 5 * 3 = 15
Creating a Shared (Dynamic) Library
Step 1: Write Code
Same source files as static library example.
Step 2: Compile with Position-Independent Code (PIC)
bash
gcc -c -fPIC mylib.c -o mylib.o
Step 3: Create Shared Library
bash
gcc -shared -o libmylib.so mylib.o
Step 4: Link Program
bash
gcc main.c -L. -lmylib -o myprogram
Step 5: Run with Library Path
bash
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. ./myprogram
Full Practical Code Example with Explanation
This program demonstrates creating and using both static and shared libraries with a comprehensive math utilities library.
Library Project Structure
text
mymath/ ├── include/ │ └── mymath.h ├── src/ │ ├── arithmetic.c │ └── geometry.c ├── main.c └── Makefile
Header File: include/mymath.h
c
/** * mymath.h * Math utilities library header. * Contains declarations for arithmetic and geometry functions. */ #ifndef MYMATH_H #define MYMATH_H /* Constants */ #define PI 3.14159 #define EULER_E 2.71828 /* Arithmetic functions */ int add(int a, int b); int subtract(int a, int b); int multiply(int a, int b); double divide(int a, int b); /* Geometry functions */ double circleArea(double radius); double circleCircumference(double radius); double rectangleArea(double length, double width); double triangleArea(double base, double height); #endif
Source File: src/arithmetic.c
c
/** * arithmetic.c * Arithmetic operations implementation. */ #include "mymath.h" int add(int a, int b) { return a + b; } int subtract(int a, int b) { return a - b; } int multiply(int a, int b) { return a * b; } double divide(int a, int b) { if (b == 0) { return 0.0; } return (double)a / b; } Source File: src/geometry.c
c
/** * geometry.c * Geometry operations implementation. */ #include "mymath.h" double circleArea(double radius) { return PI * radius * radius; } double circleCircumference(double radius) { return 2 * PI * radius; } double rectangleArea(double length, double width) { return length * width; } double triangleArea(double base, double height) { return 0.5 * base * height; } Main Program: main.c
c
/** * main.c * Main program using the mymath library. */ #include <stdio.h> #include <stdlib.h> #include "mymath.h" int main(void) { int a = 10, b = 5; double radius = 7.0; double length = 12.0, width = 8.0; double base = 6.0, height = 9.0; printf("=== Math Library Demo ===\n"); /* Arithmetic */ printf("\nArithmetic:\n"); printf("%d + %d = %d\n", a, b, add(a, b)); printf("%d - %d = %d\n", a, b, subtract(a, b)); printf("%d * %d = %d\n", a, b, multiply(a, b)); printf("%d / %d = %.2f\n", a, b, divide(a, b)); /* Geometry */ printf("\nGeometry:\n"); printf("Circle area (r=%.1f): %.2f\n", radius, circleArea(radius)); printf("Circle circumference (r=%.1f): %.2f\n", radius, circleCircumference(radius)); printf("Rectangle area (l=%.1f, w=%.1f): %.2f\n", length, width, rectangleArea(length, width)); printf("Triangle area (b=%.1f, h=%.1f): %.2f\n", base, height, triangleArea(base, height)); printf("\nPI = %.5f\n", PI); return 0; } Makefile
makefile
# Makefile for building static and shared libraries CC = gcc CFLAGS = -Wall -Wextra -std=c11 -Iinclude AR = ar # Directories SRC_DIR = src INC_DIR = include BUILD_DIR = build # Source files SRCS = $(wildcard $(SRC_DIR)/*.c) OBJS = $(SRCS:.c=.o) # Targets .PHONY: all clean static shared all: static shared # Build static library static: $(OBJS) $(AR) rcs libmymath.a $(OBJS) @echo "Static library created: libmymath.a" # Build shared library shared: $(SRCS) $(CC) $(CFLAGS) -c -fPIC $(SRCS) $(CC) -shared -o libmymath.so $(OBJS) @echo "Shared library created: libmymath.so" # Compile object files %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ # Build main program with static library program_static: main.c static $(CC) $(CFLAGS) main.c -L. -lmymath -o program_static # Build main program with shared library program_shared: main.c shared $(CC) $(CFLAGS) main.c -L. -lmymath -o program_shared # Clean build artifacts clean: rm -f $(OBJS) libmymath.a libmymath.so program_static program_shared
Build Commands
bash
# Build both libraries make all # Build and run static program make program_static ./program_static # Build and run shared program make program_shared export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. ./program_shared
Expected Output
text
=== Math Library Demo === Arithmetic: 10 + 5 = 15 10 - 5 = 5 10 * 5 = 50 10 / 5 = 2.00 Geometry: Circle area (r=7.0): 153.94 Circle circumference (r=7.0): 43.98 Rectangle area (l=12.0, w=8.0): 96.00 Triangle area (b=6.0, h=9.0): 27.00 PI = 3.14159
Common Pitfalls, Edge Cases & Memory Considerations
1. Missing Include Guards
Headers without include guards cause redefinition errors.
c
/* Bad header */ void myFunction(void); /* If included twice: error */
Solution: Always use include guards:
c
#ifndef HEADER_H #define HEADER_H /* content */ #endif
2. Forgetting -L Flag
Without -L, the linker can’t find the library.
bash
gcc main.c -lmylib -o program # Error: cannot find -lmylib
Solution: Use -L. to specify current directory:
bash
gcc main.c -L. -lmylib -o program
3. Not Setting LD_LIBRARY_PATH for Shared Libraries
Shared library not found at runtime.
bash
./program # Error: cannot open shared object file
Solution: Set LD_LIBRARY_PATH:
bash
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
4. Static Library Link Order
Library order matters for static linking.
bash
gcc -lmylib main.c -o program # May fail gcc main.c -lmylib -o program # Correct
Solution: Place libraries after source files in the command.
5. Forgetting -fPIC for Shared Libraries
Without position-independent code, shared library creation fails.
bash
gcc -c mylib.c -o mylib.o # Missing -fPIC gcc -shared -o libmylib.so mylib.o # Error
Solution: Use -fPIC when compiling:
bash
gcc -c -fPIC mylib.c -o mylib.o
6. Not Installing Library Headers
Headers must be accessible via -I flag or system directories.
bash
gcc main.c -o program # Error: mylib.h not found
Solution: Use -I to specify header directory:
bash
gcc -Iinclude main.c -L. -lmylib -o program
7. Library Naming Convention
Libraries must be named libNAME.a or libNAME.so.
Solution: Always prefix library filenames with lib.
8. Multiple Library Versions
Having multiple versions can cause conflicts.
Solution: Use versioning for shared libraries (libNAME.so.1.0.0 with symlinks).
9. Stale Object Files
Not recompiling object files after source changes.
Solution: Use make to track dependencies and rebuild as needed.
10. Memory Considerations
- Static libraries increase executable size.
- Shared libraries require runtime loading.
-fPICmay slightly increase code size.- Multiple programs share shared library memory.
Summary & Best Practices
Creating libraries in C enables code reuse, modularity, and efficient distribution. Static libraries are linked at compile time, while shared libraries are linked at runtime.
Key Takeaways
- Static libraries:
.afiles, linked at compile time. - Shared libraries:
.sofiles, linked at runtime. - Use
ar rcsto create static libraries. - Use
gcc -sharedto create shared libraries. - Use
-fPICfor position-independent code. - Use
-Lto specify library path. - Use
-Ito specify header path. - Set
LD_LIBRARY_PATHfor shared libraries at runtime.
Best Practices
- Always use include guards in header files.
- Use
-fPICfor shared libraries. - Follow naming convention:
libNAME.a/libNAME.so. - Use
-Land-Iflags for library and header paths. - Set
LD_LIBRARY_PATHfor shared libraries. - Use a Makefile to automate build process.
- Document library functions in headers.
- Version shared libraries to avoid conflicts.
- Test both static and shared linking.
- Install headers and libraries in standard locations for distribution.
By mastering library creation, you’ll write modular, reusable code and effectively distribute functionality across multiple programs.



