/commands/c/compile-multiple-c-files
gcc main.c util.c -o app: compile and link multiple C files
- tool
- C
- versions
- GCC 9+, Clang 10+
- os
- linux, macos, windows
To build a C program from several source files, pass all .c files to the compiler in one command: gcc main.c util.c -o app. Each file is compiled separately and the results are linked into one executable. Shared declarations go in a header (util.h) that both files #include; headers are never passed on the command line. For bigger projects, compile each file to an object file with -c and link the .o files.
One command, or compile and link
The example uses Clang 20 from the ziglang package on PyPI, run through uv; cc below stands for it. Write gcc or clang on your machine:
cc() { uv run -q --no-project --python 3.13 --with ziglang==0.15.1 python -m ziglang cc -fno-color-diagnostics "$@"; }
mkdir -p include src
cat > include/util.h <<'EOF'
#ifndef UTIL_H
#define UTIL_H
int add(int a, int b);
#endif
EOF
cat > src/util.c <<'EOF'
#include "util.h"
int add(int a, int b) { return a + b; }
EOF
cat > src/main.c <<'EOF'
#include <stdio.h>
#include "util.h"
int main(void) {
printf("2 + 3 = %d\n", add(2, 3));
return 0;
}
EOF
# 1. everything in one step
cc -Wall -Iinclude src/main.c src/util.c -o app
./app
# 2. compile each file, then link the objects
cc -Wall -Iinclude -c src/util.c -o util.o
cc -Wall -Iinclude -c src/main.c -o main.o
cc main.o util.o -o app2
./app2Output
2 + 3 = 5 2 + 3 = 5
-Iinclude adds the folder to the search path for #include "util.h". The include guard (#ifndef UTIL_H) keeps the header from being processed twice in one file.
Options
| Option | Effect |
|---|---|
| gcc a.c b.c -o app | Compile and link several files in one step |
| gcc -c a.c | Compile only, produce a.o |
| gcc a.o b.o -o app | Link object files |
| -I DIR | Search DIR for #include files |
| -L DIR -lname | Link libname.a or libname.so from DIR; put -l after the files that use it |
| -MMD -MP | Write .d dependency files so make knows which headers each object uses |
| ar rcs libutil.a util.o | Bundle object files into a static library |
Errors you can meet
- Undefined reference. Forgetting a file at the link step (
gcc src/main.c -o appwithoututil.c) compilesmain.cfine and then fails at linking: GNU ld reportsundefined reference to 'add', LLVM's lldundefined symbol: add. See undefined reference to, which applies to C as well. - Implicit declaration. Calling
addwithout includingutil.hgives implicit declaration of function, an error in C99 and later with current compilers. - No such file.
fatal error: util.h: No such file or directorymeans the header folder is missing from-I. - Multiple definition. A function body in a header that two
.cfiles include is defined twice; keep only the prototype in the header.
Confirm it worked
The program runs and prints the result of the function from the other file. nm main.o lists add as undefined (U) and nm util.o as defined (T), which is exactly what the linker joins.
FAQ
Should I pass the .h files to gcc?
No. Header files are pulled in by #include; you only pass the .c files. Use -I DIR when the headers live in another folder.
Why compile with -c and link separately?
Each .c file becomes an object file that only needs recompiling when it changes. Linking the objects is fast, so large projects rebuild only what changed. Build tools such as make and CMake automate exactly that.
What does multiple definition of mean?
The same function or global variable is defined in two files, often because a definition was written in a header that several .c files include. Keep only declarations in headers and the definition in one .c file.