将变量声明为引用时的堆释放后使用

heap-use-after-free when declaring a variable as a reference

下面粘贴的代码 returns 一个 heap-use-after-free 错误。当我删除带有 coord &c = q.front(); q.pop(); 的行上的引用符号 '&' 时,错误得到解决。

据我所知,C++ 垃圾收集器会在不再引用我从 q.front() 中检索到的坐标时将其删除。虽然这里看起来 coord &c 中的 c 在从队列中弹出后立即从堆中删除,并且在下一行中尝试访问 c 会导致错误。

然而,这并不是每次都会发生,所以我想知道为什么会发生这种情况。

    class Solution {
    public:
        int numIslands(vector<vector<char>>& grid) {
            if(grid.size() == 0) return 0;
            typedef pair<int,int> coord;
            queue<coord> q;
            const int m = grid.size();
            const int n = grid[0].size();
            int i_in[] = {0,0,1,-1};
            int j_in[] = {1,-1,0,0};
            int ans = 0;
            for(int i = 0; i < m; ++i)
            {
                for(int j = 0; j < n; ++j)
                {
                    if(grid[i][j] == '1')
                    {
                        ++ans;
                        q.push(coord(i,j));
                        while(q.size() > 0)
                        {
                            coord &c = q.front(); q.pop();
                            grid[c.first][c.second] = '*';
                            for(int k = 0; k < 4; ++k)
                            {
                                int newi = c.first + i_in[k];
                                int newj = c.second + j_in[k];
                                if(newi >= 0 && newi < m &&
                                   newj >= 0 && newj < n && 
                                   grid[newi][newj] == '1')
                                {
                                    q.push(coord(newi, newj));
                                }
                            }
                        }
                    }
                }
            }
            return ans;
        }
    };
coord &c = q.front(); 

^^^ 此行将 c 设置为引用当前位于队列前面的 pair<int,int>

q.pop();

^^^ 此行删除队列前面的项目,并在此过程中销毁它)。因此,在这一行 returns 之后,您的 c 引用指向一个无效对象,这意味着尝试使用 c 将调用未定义的行为。

However, this does not happen every time, so I'm wondering why this is occurring.

正在发生的事情是 undefined behavior,它会在您尝试使用悬空引用时被调用。关于未定义行为的有趣之处在于,“事情似乎工作正常”是一个有效的结果,就像发生的任何其他事情一样——因为一旦你调用了未定义的行为,所有的赌注都没有了,编译器作者没有任何使程序从此正确运行的义务,世界就坏了。

要解决此问题,您可以删除与号(就像您所做的那样),这样就没有引用,因此不会出现悬空引用问题(因为您已经复制了队列的 pair<int,int> 对象改为局部变量);或者您可以将对 q.pop() 的调用向下移动到 while 循环的末尾,这样它只会在您对 c 引用的所有使用都已经执行之后才会发生。