extern var a: T
means that a
is an alias for the symbol a:
a
will return the value of the symbol a
&a
will return the address of the symbol a.
const b = @extern(*T, .{ .name = "a" })
means that b
is a pointer to the symbol a, not an alias for it:
b
will return the address of the symbol a (not the value of a).
&b
will return the address of b
(not the address of a).
b.*
will return the value of the symbol a.
If the symbol represents a pointer and not a value, it is critical that you take into account the additional level of indirection introduced by @extern()
, which extern
doesn’t. For that reason, it can be a good idea to prefer extern
when possible.
Example (x86-64):
comptime {
asm (
\\ .data
\\ .globl foo
\\ .align 4
\\foo:
\\ .long 123
\\
\\ .text
\\ .globl getFooPtr
\\getFooPtr:
\\ lea foo(%rip),%rax
\\ ret
\\
\\ .text
\\ .globl getFoo
\\getFoo:
\\ mov foo(%rip),%eax
\\ ret
\\
\\ .text
\\ .globl setFoo
\\setFoo:
\\ mov %ecx,foo(%rip)
\\ ret
);
}
Using extern
:
const std = @import("std");
extern var foo: i32;
extern fn getFooPtr() *i32;
extern fn getFoo() i32;
extern fn setFoo(value: i32) void;
test {
try std.testing.expectEqual(&foo, getFooPtr());
try std.testing.expectEqual(123, foo);
try std.testing.expectEqual(123, getFoo());
try std.testing.expectEqual(123, getFooPtr().*);
foo = 456;
try std.testing.expectEqual(456, foo);
try std.testing.expectEqual(456, getFoo());
try std.testing.expectEqual(456, getFooPtr().*);
setFoo(789);
try std.testing.expectEqual(789, foo);
try std.testing.expectEqual(789, getFoo());
try std.testing.expectEqual(789, getFooPtr().*);
}
Using @extern()
:
const std = @import("std");
const foo = @extern(*i32, .{ .name = "foo" });
const getFooPtr = @extern(*const fn () callconv(.c) *i32, .{ .name = "getFooPtr" });
const getFoo = @extern(*const fn () callconv(.c) i32, .{ .name = "getFoo" });
const setFoo = @extern(*const fn (value: i32) callconv(.c) void, .{ .name = "setFoo" });
test {
try std.testing.expectEqual(foo, getFooPtr());
try std.testing.expectEqual(123, foo.*);
try std.testing.expectEqual(123, getFoo());
try std.testing.expectEqual(123, getFooPtr().*);
foo.* = 456;
try std.testing.expectEqual(456, foo.*);
try std.testing.expectEqual(456, getFoo());
try std.testing.expectEqual(456, getFooPtr().*);
setFoo(789);
try std.testing.expectEqual(789, foo.*);
try std.testing.expectEqual(789, getFoo());
try std.testing.expectEqual(789, getFooPtr().*);
}
Take note of how &foo
vs foo
vs foo.*
are used.