Working with C
One of the great features of Zig is that it is fully compatible with C. Let's return to the Hello, world! example, this time using C to print instead of Zig.
const c = @cImport({
@cInclude("stdio.h");
});
pub fn main() !void {
_ = c.printf("Hello, world!\n");
}
That's it! Using the cImport
directive, we can directly use C code inside of Zig. Of course, please compile this to verify that it works as expected.
Custom C code
Let's write some of our own code instead of using the C standard library. Our custom C program will have just a single greet
function, that says hello to the name passed as a parameter.
// In file /c-src/greeter.h
void greet(const char *name);
// In file /c-src/greeter.c
#include "greeter.h"
#include <stdio.h>
void greet(const char *name) {
if (name == NULL) {
printf("Hello, world!\n");
} else {
printf("Hello, %s!\n", name);
}
}
Let's verify that this works as a C program first. We can create a temporary main file for the C program.
// In file /c-src/main.c
#include "greeter.h"
#include <stdio.h>
int main() {
greet("Alice");
greet("Bob");
greet(NULL);
return 0;
}
And then compile it using gcc
as with any other C program.
$ gcc -o greet c-src/greeter.c c-src/main.c
$ ./greet
Hello, Alice!
Hello, Bob!
Hello, world!
It works! So how do we get gcc
to work with Zig? Ah! We unveil the secret behind Zig's ability to handle C code so well. It comes with a C compiler :O
$ zig cc -o greet c-src/greeter.c c-src/main.c
$ ./greet
Hello, Alice!
Hello, Bob!
Hello, world!
Insane! Let's continue by trying using this C code in Zig instead of just C. We can start by modifying our build file to look for greeter.h
in the correct place.
// In file /build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{
.name = "greet",
.root_source_file = b.path("src/main.zig"),
.target = b.standardTargetOptions(.{}),
.optimize = b.standardOptimizeOption(.{}),
});
exe.addIncludePath(b.path("c-src"));
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}
Now, we can configure our main.zig
file to import our custom greeter.c
.
// In file /src/main.zig
const c = @cImport({
@cInclude("greeter.c");
});
pub fn main() !void {
c.greet("Alice");
c.greet("Bob");
c.greet(null);
}
We use the zig build run
command instead of zig run
, and we can see that everything works well!
$ zig build run
Hello, Alice!
Hello, Bob!
Hello, world!
Last updated