# Blog: Understanding How Zig and C Interact

**URL:** <https://ziggit.dev/t/blog-understanding-how-zig-and-c-interact/6733>\
**Category:** Media\
**Tags:** compiler, c, build-system, learning\
**Created:** [November 7, 2024, 5:03am UTC](https://ziggit.dev/t/blog-understanding-how-zig-and-c-interact/6733 "2024-11-07T05:03:23Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![ramonmeza](https://ziggit.dev/user_avatar/ziggit.dev/ramonmeza/32/3712_2.png) [@ramonmeza](https://ziggit.dev/u/ramonmeza)\
**Post date:** [November 7, 2024, 5:03am UTC](https://ziggit.dev/t/blog-understanding-how-zig-and-c-interact/6733/1 "2024-11-07T05:03:23Z")

</div>

Hello everyone!

I’m a newcomer to Zig and am absolutely loving it. I wanted to explore using C libraries in Zig and what it means to creating a binding to a C library. In this post I cover a variety of topics, such as using `zig cc` to compile pure C code, compiling a static and shared library, linking to libraries and writing wrapper functions around C functions to make your C library more Zig friendly!

Please feel free to give some feedback, as I’m sure there’s plenty to learn. Thank you for your time!

Link to Github

> **[GitHub - ramonmeza/zig-c-tutorial: Learn to create Zig bindings for C libraries!](https://github.com/ramonmeza/zig-c-tutorial/?tab=readme-ov-file#understanding-how-zig-and-c-interact)**
>
> Learn to create Zig bindings for C libraries! Contribute to ramonmeza/zig-c-tutorial development by creating an account on GitHub.

---

<div class="post-metadata">

**Author:** ![pierrelgol](https://ziggit.dev/user_avatar/ziggit.dev/pierrelgol/32/10233_2.png) [@pierrelgol](https://ziggit.dev/u/pierrelgol)\
**Post date:** [November 7, 2024, 8:31am UTC](https://ziggit.dev/t/blog-understanding-how-zig-and-c-interact/6733/2 "2024-11-07T08:31:09Z")

</div>

Hey, nice post, just a small suggestion, I learned that the hard way before, but you don’t need to extern define C functions with Zig **C types** , you can just extern define the C functions, with coercible Zig type equivalent and let the compiler do the rest so for example in your little example.

```zig
const zmath_h = @cImport(@cInclude("zmath.h"));

pub extern fn add(a: c_int, b: c_int) c_int;
pub extern fn sub(a: c_int, b: c_int) c_int;

```

this wrapping can simply be done like this

```zig
const zmath = @cImport({
    @cInclude("zmath.h");
});

pub extern fn add(a: i32, b: i32) callconv(.C) i32;
pub extern fn sub(a: i32, b: i32) callconv(.C) i32;

```

I’m not even sure the `callconv(.C)` is necessary, because it works without too, but point being that C type that can safely coerce to Zig equivalent, are always correct. At least that’s my understanding, as it was recommended to me before is to basically not use the _c\_types_ directly as they are there just for the automatic translation and shouldn’t be used by us directly if I’m not mistaken.

---

<div class="post-metadata">

**Author:** ![LucasSantos91](https://ziggit.dev/user_avatar/ziggit.dev/lucassantos91/32/440_2.png) [@LucasSantos91](https://ziggit.dev/u/LucasSantos91)\
**Post date:** [November 7, 2024, 10:52am UTC](https://ziggit.dev/t/blog-understanding-how-zig-and-c-interact/6733/3 "2024-11-07T10:52:23Z")

</div>

> [@pierrelgol](#):
>
> you don’t need to extern define C functions with Zig **C types**

That’s true, but it mostly used for pointers. So , e.g., for a `[*C]T` that can only point to a single `T` and can never be null, you should use `*T`. For integers this is complicated because C allows these types to change depending on target and compiler. In some cases, `c_int` won’t translate to a `i32`, so in these cases, it’s better to use the C types.

> [@pierrelgol](#):
>
> I’m not even sure the `callconv(.C)` is necessary

It is, because Zig reserves the right to use any calling convention when not instructed to use one in particular, which could improve performance. Currently, I think it doesn’t actually leverage this, and always use the platform ABI.

---

<div class="post-metadata">

**Author:** ![pierrelgol](https://ziggit.dev/user_avatar/ziggit.dev/pierrelgol/32/10233_2.png) [@pierrelgol](https://ziggit.dev/u/pierrelgol)\
**Post date:** [November 7, 2024, 11:22am UTC](https://ziggit.dev/t/blog-understanding-how-zig-and-c-interact/6733/4 "2024-11-07T11:22:34Z")

</div>

Oh yes absolutely, I completely forgot about the fact that c\_int is dependent on the platform, I was just trying to show that you can “coerce” the function signature to true Zig type directly, meaning you don’t necessarily have to extern define it with c\_types and then wrap it inside of a Zig function. But thanks for pointing that, my bad for the bad example.

---

<div class="post-metadata">

**Author:** ![ramonmeza](https://ziggit.dev/user_avatar/ziggit.dev/ramonmeza/32/3712_2.png) [@ramonmeza](https://ziggit.dev/u/ramonmeza)\
**Post date:** [November 7, 2024, 4:53pm UTC](https://ziggit.dev/t/blog-understanding-how-zig-and-c-interact/6733/5 "2024-11-07T16:53:46Z")

</div>

I see, thanks for your input.

With type coercion, I don’t think I am able to return error types directly from the C function, so I think I’d still need a separate way of wrapping the methods in Zig code. Here’s what I mean:

```c
pub extern fn myCFun(a: i32, b: i32) i32; // this works
pub extern fn myCFun(a: i32, b: i32) !i32; // this doesn't work (ofc)

```

If the function was any more complex, I think returning an error type is still beneficial, but in the simple example in the blog post its very much overkill. I’ll look more into callconv(.C), as I’ve seen it, but am not too sure what it’s purpose is.

Thank you!

---

<div class="post-metadata">

**Author:** ![ramonmeza](https://ziggit.dev/user_avatar/ziggit.dev/ramonmeza/32/3712_2.png) [@ramonmeza](https://ziggit.dev/u/ramonmeza)\
**Post date:** [November 7, 2024, 4:57pm UTC](https://ziggit.dev/t/blog-understanding-how-zig-and-c-interact/6733/6 "2024-11-07T16:57:52Z")

</div>

Speaking of `callconv()`, is this required when calling C code from Zig? Looking through [zig.guide’s explanation of `callconv`](https://zig.guide/working-with-c/calling-conventions/) seems to suggest that it’s only necessary when calling Zig code from C.

If you have any insight, I’d greatly appreciate it. Thanks 🙂

---

<div class="post-metadata">

**Author:** ![LucasSantos91](https://ziggit.dev/user_avatar/ziggit.dev/lucassantos91/32/440_2.png) [@LucasSantos91](https://ziggit.dev/u/LucasSantos91)\
**Post date:** [November 7, 2024, 5:36pm UTC](https://ziggit.dev/t/blog-understanding-how-zig-and-c-interact/6733/7 "2024-11-07T17:36:08Z")

</div>

It is. The calling convention is the ABI. When caller and calle are both in Zig, Zig can decide whichever calling convention it wants to use. When interacting with C, either calling or being called, there needs to be a well-defined and explicit ABI, otherwise there’s no guarantee that both functions are speaking the same language.
