I want to create a big 2D array. Because the array is fairly big, I’m trying to allocate the majority of the data on the heap. But for some reason I’m causing a segmentation fault in the program, and I’m not really sure why.
The segmentation fault happens on the stdout.print() line.
Is amazing how we forget the most basic things sometimes. The program is causing a segmentation fault, because when I try to access the element of the array in the stdout.print line, the array does not exist anymore, because it was deallocated by the free() operation that is executed at the end of the for loop scope.
So, you can fix the program by either:
following @jmc suggestion on transforming the defer’s in errdefer’s.
or moving the free operations to the end of the main’s function scope.
Just curious. Would that not open the code up to a possible memory leak if an error occurred somewhere in the above for loops and it never reached that last for loop? If that could be possible then what would the solution be then?
I realize that in this particular example, it might not be possible, but say the code was more complex.
Of course, this would only work for reasonably small values of n and only live as long as the scope they’re defined in.
Since this is a case of more complex initialization/deinitialization (e.g. the allocation of the rows of this 2D array could fail after some other allocations have succeeded, and you would need to ensure proper cleanup). For that I would consider using helper methods to allocate/free the array as needed and properly handle edge cases so that they can be used in defer/errdefer contexts:
You might consider to actually allocate arrays of size n*n instead of arrays of slices. Then you have way fewer allocations to create / clean up. On the downside, the 2D index math is now on you:
const std = @import("std");
const stdout = std.io.getStdOut().writer();
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const Random = std.crypto.random;
const n: usize = 100;
var A: []u64 = try allocator.alloc(u64, n * n);
defer allocator.free(A);
var B: []u64 = try allocator.alloc(u64, n * n);
defer allocator.free(B);
var C: []u64 = try allocator.alloc(u64, n * n);
defer allocator.free(C);
for (0..n) |i| {
for (0..n) |j| {
const random_num = Random.int(u8);
A[i * n + j] = random_num;
B[i * n + j] = random_num;
C[i * n + j] = 0;
}
}
try stdout.print("{any}\n", .{A[0 * n + 0]});
}
I think the best solution would be to allocate the entire 2d array in one piece on the heap, instead of allocating them in parts. This is much simpler and should give you a better memory layout: