@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