将变量声明为引用时的堆释放后使用
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
引用的所有使用都已经执行之后才会发生。
下面粘贴的代码 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
引用的所有使用都已经执行之后才会发生。