@rep_stosq_void on Twitter posted this strange sample of C code, and I wanted to show my process of understanding this contrived C code.
https://twitter.com/rep_stosq_void/status/1446504706319294475
After running zig translate-c x.c > x.zig
, I got the following output:
pub export fn f(arg_a: [*c]c_int) void {
var a = arg_a;
var p: ?*c_void = @ptrCast(?*c_void, &a);
@ptrCast(
[*c][*c]c_int,
@alignCast(@import("std").meta.alignment([*c]c_int), &@ptrCast(
[*c][*c][*c]c_int,
@alignCast(@import("std").meta.alignment([*c][*c]c_int), p),
).*),
).*.* = 1;
}
From here I removed the unnecessary calls to std.meta.alignment
by adding alignment data to p
. I also converted [*c]T
pointers to [*]T
and *T
, depending on my assumptions of how the code works. I also removed the optional from p
, and stripped away arg_a
.
Now we've got something a bit more faithful to the original.
fn f(a: *c_int) void {
const p = @ptrCast(*align(@alignOf(usize)) const c_void, &a);
@ptrCast(
*const [*]c_int,
&@ptrCast(*const *[*]c_int, p).*,
).*.* = 1;
}
We can now transform &@ptrCast(*const *[*]c_int, p).*
into @ptrCast(*const *[*]c_int, p)
, as &x.*
is equivalent to x
.
fn g(a: *c_int) void {
const p = @ptrCast(*align(@alignOf(usize)) const c_void, &a);
@ptrCast(
*const [*]c_int,
@ptrCast(*const *[*]c_int, p),
).*.* = 1;
}
As we can see p
is a pointer to the argument a
, we can change its ptrCast to the more appropriate type *const *i32
. The const is needed as arguments are immutable.
fn h(a: *c_int) void {
const p = @ptrCast(*const *i32, &a);
@ptrCast(
*const [*]c_int,
@ptrCast(*const *[*]c_int, p),
).*.* = 1;
}
After substituting in p
.
fn i(a: *c_int) void {
@ptrCast(
*const [*]c_int,
@ptrCast(
*const *[*]c_int,
@ptrCast(*const *i32, &a),
),
).*.* = 1;
}
There's some many-pointers ([*]T
) here, which we should replace with single pointers (*T
). This is because I can tell that there aren't really any arrays at play here.
fn j(a: *c_int) void {
@ptrCast(
*const *c_int,
@ptrCast(
*const **c_int,
@ptrCast(*const *i32, &a),
),
).*.* = 1;
}
Here we can remove the unnecessary ptrCast around &a
, as &a
is already of type *const *i32
.
fn k(a: *c_int) void {
@ptrCast(
*const *c_int,
@ptrCast(
*const **c_int,
&a,
),
).*.* = 1;
}
Here we can see a ptrCast from *const *i32
to *const **c_int
, back to *const *c_int
. Let's remove that.
fn l(a: *c_int) void {
(&a).*.* = 1;
}
Finally, we can transform (&x).*
into x
.
fn m(a: *c_int) void {
a.* = 1;
}
Perhaps the answer is disappointing.
Top comments (3)
Kinda, yeah, lol.
You translate 1 line of C mess to 7 lines of Zig mess which are then manually simplified through transforms. Why not just apply transforms to the original C code?
I answered this here news.ycombinator.com/item?id=29099284