digitalmars.D.learn - Why am I getting segfaults when doing `foreach` with arrays of
- Liam McGillivray (74/74) Mar 08 With [my game project](https://github.com/LiamM32/Open_Emblem), I
- Richard (Rikki) Andrew Cattermole (16/16) Mar 08 Something that I have noticed that you are still doing that was pointed
- Liam McGillivray (11/22) Mar 08 I think I'm starting to understand better. I was thrown off
- Richard (Rikki) Andrew Cattermole (15/20) Mar 08 The default initialization state of a pointer (regardless of type) in D
- Liam McGillivray (6/11) Mar 09 I'll answer my own question; because the thing assigned to the
With [my game project](https://github.com/LiamM32/Open_Emblem), I have been getting segmentation faults that are unexplainable at my knowledge level. They seem to happen when doing a "foreach" loop through an array of references. Skip to the bolded text if you don't want to read too much, as I found a way around the first example. Prior to the latest commit, I had this "foreach" loop lines starting at `oe-raylib/source/mission.d:214` ([see file](https://github.com/LiamM32/Open_Emblem/blob/09505794970168c68ed5cffd9aa2ed69490ee80e/oe-raylib source/mission.d)). It would segfault on the second line shown here: ``` foreach (startTile; this.startingPoints) { startTile.occupant.map = this; writeln("Just assigned Unit.map to the Mission object."); this.factionUnits["player"] ~= *startTile.occupant; } ``` This was in the `Mission` class (a derivative of `Map`), which `this` would be a reference to. `startTile` is an instance of the `GridTile` struct, which contains a reference to a `Tile` object called `tile`. The `Tile` object had a pointer to a `Unit` object called `occupant`. `GridTile.occupant` is a convenience function that's equivalent to `GridTile.tile.occupant`. Finally, the `Unit` class contains a reference to a `Map` object called `map`. Because `Mission` is a derived class, a `Mission` object can also fit. All of the things being accessed were public. I thought the problem was that it was looking in `Tile` objects with `occupant` being null, but changing line 214 to the following didn't solve it. ``` foreach (startTile; this.startingPoints) if (startTile.occupant !is null) { ``` **Current example** I ended up getting rid of that line causing the segmentation fault and achieving the desired functionality a different way, but now I get a similar one later in the program. The `turnReset` function in my [`Map` class](https://github.com/LiamM32/Open_Emblem/blob/master/source/map.d) does a 'foreach' loop through an array of `Unit` objects: ``` void turnReset() { foreach(unit; this.allUnits) { unit.turnReset; } } ``` My program successfully completed one cycle of the loop, but segfaulted with the second one. I found in GDB that for the second object it didn't even reach the first line in `Unit.turnReset()`. This may imply that the array length of `Map.allUnits` is more than one, but no object has been assigned to `allUnits[1]`. However, there are no lines anywhere in my code that changes the length of this array without appending. I tried doing the `grep -r allUnits` command to look for every mention of this array in my code, and here is what I have: ``` source/map.d: public Unit[] allUnits; source/map.d: foreach (unit; this.allUnits) { source/map.d: foreach(unit; this.allUnits) { source/map.d: assert (canFind(map.allUnits, unit)); source/unit.d: if (this in map.allUnits) { oe-raylib/source/mission.d: this.allUnits ~= *startTile.occupant; oe-raylib/source/mission.d: foreach (unit; this.allUnits) { oe-raylib/source/mission.d: if (addToMap) allUnits ~= newUnit; oe-raylib/source/mission.d: foreach (unit; mission.allUnits) { oe-raylib/source/mission.d.bak2: foreach (unit; this.allUnits) { ``` As far as I see, there's no way that the `allUnits` array would be longer than the number of objects that have been added to it, so why would calling a public function of one of it's members cause a segfault?
Mar 08
Something that I have noticed that you are still doing that was pointed out previously is having a pointer to a class reference. Stuff like ``Tile* currentTile;`` when it should be ``Tile currentTile;`` A class reference is inherently a pointer. So when you checked for nullability in the foreach loop of mission: ```d if (startTile.occupant !is null && (*startTile.occupant) !is null) { ``` is what it should've looked like. ```d import std.stdio : writeln; void main() { Object o = new Object; writeln(cast(void*)o); // 7F2780EFD000 } ```
Mar 08
On Saturday, 9 March 2024 at 06:37:02 UTC, Richard (Rikki) Andrew Cattermole wrote:Something that I have noticed that you are still doing that was pointed out previously is having a pointer to a class reference. Stuff like ``Tile* currentTile;`` when it should be ``Tile currentTile;`` A class reference is inherently a pointer. So when you checked for nullability in the foreach loop of mission: ```d if (startTile.occupant !is null && (*startTile.occupant) !is null) { ```I think I'm starting to understand better. I was thrown off somewhere when I read that "references in D cannot be null". I tried doing some if-statements to determine if array objects are null, and some of them were. But that begs the question; why? Don't dynamic arrays always start with a length of 0? If the array was only extended when valid objects were appended using the append operator `~=`, and none of those objects were deleted (as I the destructor was never called), why would some of the array elements be null?
Mar 08
On 09/03/2024 8:49 PM, Liam McGillivray wrote:But that begs the question; why? Don't dynamic arrays always start with a length of 0? If the array was only extended when valid objects were appended using the append operator |~=|, and none of those objects were deleted (as I the destructor was never called), why would some of the array elements be null?The default initialization state of a pointer (regardless of type) in D is null. I.e. ```d Object[] array; array.length = 2; array[0] = new Object; assert(array[0] !is null); assert(array[1] is null); array ~= new Object; assert(array[0] !is null); assert(array[1] is null); assert(array[2] !is null); ```
Mar 08
On Saturday, 9 March 2024 at 07:49:52 UTC, Liam McGillivray wrote:But that begs the question; why? Don't dynamic arrays always start with a length of 0? If the array was only extended when valid objects were appended using the append operator `~=`, and none of those objects were deleted (as I the destructor was never called), why would some of the array elements be null?I'll answer my own question; because the thing assigned to the array was already null. Anyway, I managed to fix the segfaults. In the latest two commits, I have turned some pointers into references. Now that I understand this, I should have fewer segmentation faults.
Mar 09