It would be cool if you gave the actual code snippets where the routine happens rather than just a summary. Not that I don't appreciate the explanation--I'd just like to see how it's actually done, if only because Carmack's code is always interesting to read.
From my (limited) experience - using indexes instead of pointers has additional advantage - it makes it super easy to serialize/deserialize arbitrary graphs of objects.
Save game if you use index for referencing objects = one fwrite call.
Pointers can be easily mapped from and into integer indices or file offsets in-place, so there is no big difference really.
I think it is generally not a good idea when in-memory representation of objects is identical to the serialized representation. This approach tends to backfire in a number of ways, such as format incompatibility between software versions, or hardware architecture differences, so it is always better to assume that your serialized representation will be different from in-memory representation.
This echoes the tribulations of the original Starcraft devs. [1]
> ...based on my experiences the biggest problems in StarCraft related to the use of doubly-linked linked lists. Linked lists were used extensively in the engine to track units with shared behavior. [...] Some of the link fields were shared among several lists, so it was necessary to know exactly which list an object was linked into in order to safely unlink. And some link fields were even stored in C unions with other data types to keep memory utilization to a minimum. So the game would blow up all the time. All the time.
Serialising game state to disk was done by moving all those scattered objects into a contiguous memory block, re-link, save to disk, and then restore the game engine's internal state.
> It was necessary to fixup all the link pointers (taking special care of unioned pointer fields) so that all 1600 units could be written at once. And then unfixup the link pointers to keep playing. Yuck.
Adding a dead boolean field, then all the entities with links removes their pointers when possible by looking at the dead field, at the end the object is freed.