I’ve run into a rather confusing issue where an array and it’s respective slice seem to be pointing at two different things. I am writting a CHIP-8 emulator and I was trying to do some conversion from u8 array to u16 array, in the middle I decided to use a slice rather than an array but started getting errors through the code so I slipped in a few asserts
The memory_s is assigned to on init and then never changed again
This is the error
C:\Programs\zig\zig-windows-x86_64-0.14.0-dev.3062+ff551374a\lib\std\debug.zig:518:14: 0x7ff79d3b71ed in assert (Chip8.exe.obj)
if (!ok) unreachable; // assertion failure
^
D:\Coding\Chip8\src\emulator.zig:389:21: 0x7ff79d3b57f7 in currentInstruction (Chip8.exe.obj)
std.debug.assert(self.memory[self.execute_cursor] == self.memory_s[self.execute_cursor]);
The repo in question in the specidied branch oddError:
At first I believed it’s just another mess up on my part so I went around the code trying to ensure that It’s not broken. But the pointer address of the slice not being the same as the array while debugging had me thinking this could be compiler related. So to be on the safe side, I decided to mention it here first.
pub fn init(program_bytes: []const u8) !Emulator {
// ...
var emu: Emulator = .{
// ...
};
// ...
emu.memory_s = &emu.memory; // points to the current stack frame
// ...
return emu; // this returns a copy of 'emu', including the `emu.memory` array
}
// ...
fn foo() void {
const emu = try Emulator.init(bytes);
// 'emu.memory' resides on the 'foo' stack frame, but
// 'emu.memory_s' points to data on the 'init' stack frame,
// which no longer exists and will be overwritten by junk
}
In summary:
emu is initialized and stored on the init function’s stack frame. This includes the emu.memory field (important reminder: arrays are values in zig, they don’t decay into pointers like in C).
A pointer pointing to emu.memory is then stored to emu.memory_s, meaning that emu.memory_s is pointing to the current stack frame.
Finally, emu is returned to the caller (the foo function), meaning that its data is copied by value to the result location. After this, the init stack frame is no longer active and any data residing on it invalidated.
The emu inside the foo function’s memory_s field is pointing to data that is no longer valid and which may be overwritten by any random junk data.
I would advice against defining self-referential structs (meaning structs with fields that point to the struct instance itself) as they are very error-prone and easy to make mistakes with when struct instances are copied around without updating pointers.
The memory_s field is pretty redundant; just pass &emu.memory directly instead.