-2

The code goes like this. It's a BFS, and I want it to go backwards, so I try to have a connection between steps using a pointer.

sx, sy are our starting points. tx, ty are our destiny points. act[][] is a Boolean table to ensure that I won't go into the same place twice.

struct trip
{
    int first;
    int second;
    trip* last;
};

void write(trip a)
{
    cout<<"\n";
    cout<< "x" << " " << a.first << "\n";
    cout<< "y" << " " << a.second << "\n";
    cout<< "x lasta" << " " << a.last->first << "\n";
    cout<< "y lasta" << " " << a.last->second << "\n";
}

queue<trip> Q;
trip P; P.first = sx; P.second = sy; P.last = nullptr; Q.push(P);
while(!Q.empty())
{
    //cout << "WOW" << "\n";
    trip temp = Q.front(); Q.pop();
    if(temp.last != nullptr)
        write(temp);

    int corx = temp.first;  int cory = temp.second;

    if(corx == tx && cory == ty)
    {
        // We found our destiny
    }

    if(act[corx][cory] == false)
    {
        act[corx][cory] = true;

        // Up
        if(cory - 1 >= 0)
        {
            trip TEMPUUS; TEMPUUS.first = corx - 1; TEMPUUS.second = cory; TEMPUUS.last = &temp; Q.push(TEMPUUS);
            cout << corx - 1 << " " << TEMPUUS.last -> first; // corx - 1 here is equal to
            //TEMPUUS.last -> first - 1 and that is what I want
        }

        // Down
        if(cory + 1 <= 2000)
        {
            trip TEMPUUS; TEMPUUS.first = corx + 1; TEMPUUS.second = cory; TEMPUUS.last = &temp; Q.push(TEMPUUS);
        }

        // Left
        if(corx - 1 >= 0)
        {
            trip TEMPUUS; TEMPUUS.first = corx; TEMPUUS.second = cory - 1; TEMPUUS.last = &temp; Q.push(TEMPUUS);
        }

        // Right
        if(corx + 1 <= 2000)
        {
            trip TEMPUUS; TEMPUUS.first = corx; TEMPUUS.second = cory + 1; TEMPUUS.last = &temp; Q.push(TEMPUUS);
        }
    }
}

So what's the problem? The problem is that when I push a trip variable to the queue its coordinates are different from its predecessor's. However, as we go into the next iteration of the queue, the temp variable goes out of scope and the "last" pointer now points at itself.

Enter image description here

There is a code sample:

#include <iostream>
#include <fstream>
#include <queue>
using namespace std;

struct trip
{
    int first;
    int second;
    trip* last;
};

void write(trip a)
{
    cout << "\n";
    cout <<  "x" << " " << a.first << "\n";
    cout <<  "y" << " " << a.second << "\n";
    cout <<  "x lasta" << " " << a.last->first << "\n";
    cout <<  "y lasta" << " " << a.last->second << "\n";
}

void bfs(int sx, int sy, int tx, int ty)
{
    queue<trip> Q;
    trip P; P.first = sx; P.second = sy; P.last = nullptr; Q.push(P);
    int test = 0;
    while(!Q.empty())
    {
        trip temp = Q.front(); Q.pop();
        if(temp.last != nullptr)
            write(temp);
        int corx = temp.first;
        int cory = temp.second;

        if(act[corx][cory] == false)
        {
            act[corx][cory] = true;

            // Up
            if(cory - 1 >= 0)
            {
                trip TEMPUUS; TEMPUUS.first = corx - 1; TEMPUUS.second = cory; TEMPUUS.last = &temp; Q.push(TEMPUUS);
                cout << corx - 1 << " " << TEMPUUS.last -> first;
            }
        }
        test++;
        if(test > 5)
            return;
    }
}

int main()
{
    int sx, sy, tx, ty;
    cin >> sx >> sy >> tx >> ty;
    sx += 1000; sy += 1000; tx += 1000; ty += 1000;
    bfs(sx, sy, tx, ty);
    return 0;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Milosz
  • 47
  • 5
  • 4
    Your title already describes exactly what the problem is. The solution is not to put a pointer to a variable that goes out of scope on the queue. – interjay Aug 05 '23 at 10:05
  • 1
    Obviously the answer is not to use a pointer in the first place. Find another way to save your path, like an array of `trip` – john Aug 05 '23 at 10:05
  • I know that pointers used in this way may be hard to work as a solution here but I'm just curious if there is any smart idea of using pointers there – Milosz Aug 05 '23 at 10:14
  • 1
    _"if there is any smart idea of using pointers there"_ Nope. @Milosz – πάντα ῥεῖ Aug 05 '23 at 10:32
  • 2
    No I still see a lot of "useless use" of pointers out there. Mainly due to out-of-date examples, lack of knowlegdge about move semantics and containers and modern C++ techniques by teachers (and historic online examples). If you are going to use raw pointers then use them only when they are non-owning. – Pepijn Kramer Aug 05 '23 at 10:41
  • Just have a look at : [the C++ core guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) and lookup anything about pointers (and raw new/delete). – Pepijn Kramer Aug 05 '23 at 10:42
  • 3
    If you pass a pointer around, *your* code/design has to somehow ensure that the pointee exists as long as the pointer does so, if pointee ceases to exist, the pointer no longer exists. One approach is to define them both in the same scope so they cease to exist at the same time. Another is that both are members of an object so they both exist as long as the object does. However, your code is passing a pointer around by copying its value, but not ensuring that the pointee still exists as long as the pointer [or all copies of it] does - so you need to redesign. – Peter Aug 05 '23 at 10:47
  • C++ [storage duration](https://en.cppreference.com/w/cpp/language/storage_duration) explains the difference between **automatic** storage and **dynamic** storage. Any pointer becomes a dangling pointer (and I think, technically, the pointer itself is invalidated, but that's not checked by the compiler or at runtime, it's the programmer's responsibility and makes for an excellent footgun) when the object it points to is destructed. – Eljay Aug 05 '23 at 11:59
  • Re *"destiny"*: Do you mean *"[destination](https://en.wiktionary.org/wiki/destination#Noun)"*? – Peter Mortensen Aug 06 '23 at 10:39

1 Answers1

2

The temp structure is allocated on the stack and when the block in which it was defined is exited, that memory is freed again. This means that the queued trip has now an invalid last reference.

That same memory gets reused in the next iteration of the loop, making the above mentioned last reference valid again. But now the assignment to temp.first affects that memory. In short, all non-null last members reference the same memory location.

To solve this, work with heap allocated memory (using new, class, ...etc).

You should also take care of freeing the used memory. As in your case the links with last form a tree, this can be quite a burden. I would suggest leaving the memory management to shared smart pointers.

Not your question, but:

  • There is an obvious error in your code. While the comment and if condition indicate an "up" movement, you make a Trip with a decreased x-coordinate instead of decreased y-coordinate.
  • Why using namespace std; is considered bad practice
  • Don't put multiple statements on the same line; certainly not when it needs scrolling to the right (one of your multi-statement lines is more than 110 characters long). Luckily using constructors removes the "need" for some of those.

Here is the replacement for struct trip and the write function:

class Trip
{
public:
    int first;
    int second;
    std::shared_ptr<Trip> last;

    Trip(int first, int second, std::shared_ptr<Trip> last) : 
            first(first), second(second), last(last)
    {
    }

    void write()
    {
        std::cout<<"\n";
        std::cout<< "x" << " " << this->first << "\n";
        std::cout<< "y" << " " << this->second << "\n";
        if (this->last) {
            std::cout<< "x lasta" << " " << this->last->first << "\n";
            std::cout<< "y lasta" << " " << this->last->second << "\n";
        }
    }
};

Now your bfs function can look like this:

void bfs(int sx, int sy, int tx, int ty)
{
    std::queue<std::shared_ptr<Trip>> Q;
    std::shared_ptr<Trip> start(new Trip(sx, sy, nullptr));
    Q.push(start);
    while (!Q.empty())
    {
        std::shared_ptr<Trip> current = Q.front();
        Q.pop();
        current->write();
        int corx = current->first;
        int cory = current->second;
        if (!act[corx][cory])
        {
            act[corx][cory] = true;
            //up
            if (cory - 1 >= 0)
            {
                std::shared_ptr<Trip> neighbor(new Trip(corx, cory - 1, current));
                Q.push(neighbor);
            }
        }
    }
}
trincot
  • 317,000
  • 35
  • 244
  • 286