A program is made from object code.
Some object code comes from your Zig files. Some may come from the standard library. Some may come from C libraries, system libraries, or other compiled files.
Linking combines those pieces into one result.
There are two common forms:
static linking
dynamic linkingWith static linking, library code is copied into the final executable.
With dynamic linking, the executable records that it needs a shared library at run time.
A static executable is often easier to move:
zig build-exe main.zig -target x86_64-linux-musl -staticThis tries to produce a Linux executable that does not depend on a separate libc shared object.
A dynamic executable is often smaller:
zig build-exe main.zig -target x86_64-linux-gnuOn a glibc Linux target, this normally uses dynamic system libraries unless told otherwise.
The difference matters when deploying programs.
A dynamically linked executable may fail if the target machine does not have the right shared library.
A statically linked executable is larger, but carries more of what it needs.
Zig exposes linking choices through the command line and through build.zig.
A simple C library example:
int add(int a, int b) {
return a + b;
}Save it as add.c.
Now call it from Zig:
const std = @import("std");
extern fn add(a: c_int, b: c_int) c_int;
pub fn main() void {
const n = add(20, 22);
std.debug.print("{d}\n", .{n});
}Build both files together:
zig build-exe main.zig add.cThe Zig compiler compiles the C file, then links the result with the Zig object code.
For a system library, use -l:
zig build-exe main.zig -lc-lc asks the linker to link libc.
In build.zig, the same idea is expressed as build settings:
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "main",
.root_module = b.createModule(.{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
}),
});
exe.linkLibC();
b.installArtifact(exe);
}linkLibC tells the build system to link libc.
The source code should not depend on the linking mode unless it must. Most Zig code can be written the same way for static and dynamic linking.
The build configuration decides how the final executable is assembled.
The important questions are practical:
Does the program need libc?
Does it need another C library?
Will the target machine have that library?
Should the executable carry the code itself?Zig makes these choices explicit. That is useful because hidden linking rules often become deployment bugs.
Exercise 17-17. Build a small program dynamically for your native target.
Exercise 17-18. Build the same program statically for x86_64-linux-musl.
Exercise 17-19. Write a C function and call it from Zig.
Exercise 17-20. Add exe.linkLibC() to a build.zig file.