Using Zig and Translate-C to understand weird C code
source link: https://zig.news/sobeston/using-zig-and-translate-c-to-understand-weird-c-code-4f8
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Using Zig and Translate-C to understand weird C code
Oct 9
・2 min read
@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.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK