Skip to content

/commands/c/compile-multiple-c-files

gcc main.c util.c -o app: compile and link multiple C files

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
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.

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:

bash
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
./app2

Output

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

OptionEffect
gcc a.c b.c -o appCompile and link several files in one step
gcc -c a.cCompile only, produce a.o
gcc a.o b.o -o appLink object files
-I DIRSearch DIR for #include files
-L DIR -lnameLink libname.a or libname.so from DIR; put -l after the files that use it
-MMD -MPWrite .d dependency files so make knows which headers each object uses
ar rcs libutil.a util.oBundle object files into a static library

Errors you can meet

  • Undefined reference. Forgetting a file at the link step (gcc src/main.c -o app without util.c) compiles main.c fine and then fails at linking: GNU ld reports undefined reference to 'add', LLVM's lld undefined symbol: add. See undefined reference to, which applies to C as well.
  • Implicit declaration. Calling add without including util.h gives implicit declaration of function, an error in C99 and later with current compilers.
  • No such file. fatal error: util.h: No such file or directory means the header folder is missing from -I.
  • Multiple definition. A function body in a header that two .c files 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.

Sources