www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why am I getting segfaults when doing `foreach` with arrays of

reply Liam McGillivray <yoshi.pit.link.mario gmail.com> writes:
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
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
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
parent reply Liam McGillivray <yoshi.pit.link.mario gmail.com> writes:
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
next sibling parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
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
prev sibling parent Liam McGillivray <yoshi.pit.link.mario gmail.com> writes:
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