#what actually gets allocated here?

1 messages · Page 1 of 1 (latest)

wooden gull
#
const Vec2 = [2]f64;

const Level = struct { points: []Vec2 };

var l = try allocator.create(Level);
defer allocator.destroy(l);

std.debug.print("points: {d}\n", .{l.points.len});

output:

points: 12297829382473034410

Is l.points.len just garbage data, or did the allocator really allocate enough memory to hold 12297829382473034410 items? 😮

modern fulcrum
#

It allocated space for a Level only.

wooden gull
#

ahh ok

sweet mural
#

it's uninitialized. if you want to allocate the points you would need l.points = try allocator.alloc(n, Vec2);

wooden gull
#

so .len just completely has garbage in it

sweet mural
#

it's 0xaaaaa... since you're in debug mode

modern fulcrum
#

The allocator returns uninitialised memory, indeed.

sweet mural
wooden gull
#

ok...so if I want to build up this struct, I'd need to allocate each of those slices individually

sweet mural
#

usually you would write an init function inside the struct but yeah

wooden gull
#

ok, that makes sense

#

so when it's time to free this

#

I'm assuming it depends on the allocator I'm using

#

arena is easy obviously, I just deinit the arena and it all goes poof

#

but if I use something like the gpa, how do I handle that?

#

do I write my own custom .free(T,allocator) function and have it individually destroy each slice?

modern fulcrum
#

free the field, destroy the Level.

south brook
#

You'd ideally have a deinit function for the type, that would also free all the fields inside

sweet mural
#

yep

wooden gull
#

ok so I should provide a deinit that bundles all this up. makes sense

sweet mural
#

btw in this example it doesn't seem necessary to heap allocate the Level itself

wooden gull
#

@modern fulcrum can you elaborate on the free vs destroy please?

south brook
#

free is used for pointers of many items. destroy is for pointers of a single item.

sweet mural
#

and that could cause issues down the road because if your deinit function takes a pointer, it would be pretty easy to accidentally give an allocator a pointer that it didn't allocate in the first place

modern fulcrum
wooden gull
#

I dumbed down the example, I'm heap allocating it because it's created inside a function and I want to pass it the caller without it getting removed upon function completion

modern fulcrum
#

ally.free/ally.destroy

sweet mural
#

create() then destroy(), or alloc() then free()

sweet mural
#

it will still hold all the slices wherever they have been allocated

wooden gull
#

it will?

#

so the returned object is not freed?

modern fulcrum
#

Returning by value will copy it to the caller's stack.

wooden gull
#

ahhh

modern fulcrum
#

Just as for primitives

wooden gull
#

sorry for these incredibly n00b questions

sweet mural
#

all good! memory is weird

wooden gull
#

I knew it worked for primitives but didn't realize it would work for other types too

#

I learned just enough c 25 years ago to know that I hated it

sweet mural
wooden gull
#

now zig has come along and made this way more pallettable

sweet mural
#

where you would have an issue would be if you returned a pointer to a local variable. then the pointer would be invalidated after the function returned.

#

but returning the value is fine

wooden gull
#

well, ok free is a bad term. I was talking about it being removed from the stack when the fn context gets popped

south brook
#

In the above case, Level is fine (because it will be copied to the caller's stack), but the slice it contains needs to be allocated. (else it'll become invalidated)

wooden gull
#

cool cool

modern fulcrum
wooden gull
#

that explains why the zmath lib returns Vectors directly too

sweet mural
#

so in summary your level would probably look something like this

pub fn init(allocator: Allocator, n: usize) !Level {
    return .{ .points = try allocator.alloc(n, Vec2) };
}

pub fn deinit(self: *Level, allocator: Allocator) void {
    allocator.free(self.points);
    self.* = undefined; // not required, but useful in debug mode to catch use-after-free
}
wooden gull
#

nice

#

ok this all clears up some 🧠 goblins

#

thank you!

sweet mural
wooden gull
#

why do that? just to make it explicit and obvious that there's a use-after-free situation being attempted?

sweet mural
#

yes

#

any access to points[x] has to check if the index is in bounds anyway (unless you made an unsafe build) so you may as well have it check for more errors

wooden gull
#

interesting!

tired nexus
#

releasefast doesn't compile any panic handler or anything afaik

#

unless you do @setRuntimeSafety(true) in the relevant block

sweet mural
#

i guess i meant releasesafe

#

cuz in debug if you set it to undefined then any access is going to dereference 0xaaaa and probably fail

#

but in releasesafe it will UAF