标准移动错误?如果将赋值副本嵌套在迭代多次的循环中,则向量引用的标准向量赋值会出现堆错误
std move bug? heap error with std vector assignment of a vector reference if nesting the assignment copy within a loop that iterates more than once
我遇到堆运行时错误。我怀疑它是过度使用 move 时的编译器错误。
代码的简化版本是
vector<vector<int>>result{{1,2,3}};
int size = result.size();
for(auto jdx = size-1; jdx >= 0; --jdx){
vector<int> &row = result[jdx];
// vector<int> newrow(row);
for(int idx = 2; idx >=1; --idx) {
vector<int> newrow(row);
result.push_back(newrow);
}
}
如果我将 newrow(row) 行换出最内层的循环,就可以像下面这样,
矢量<矢量>结果{{1,2,3}};
int size = result.size();
for(auto jdx = size-1; jdx >= 0; --jdx){
vector<int> &row = result[jdx];
vector<int> newrow(row);
for(int idx = 2; idx >=1; --idx) {
//vector<int> newrow(row);
result.push_back(newrow);
}
}
转储错误如下
=================================================================
==31==ERROR: AddressSanitizer: heap-use-after-free on address 0x6040000000b0 at pc 0x00000034789d bp 0x7ffd3434d6b0 sp 0x7ffd3434d6a8
READ of size 8 at 0x6040000000b0 thread T0
#4 0x7f9a26d790b2 (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
0x6040000000b0 is located 32 bytes inside of 48-byte region [0x604000000090,0x6040000000c0)
freed by thread T0 here:
#5 0x7f9a26d790b2 (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
previously allocated by thread T0 here:
#6 0x7f9a26d790b2 (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
Shadow bytes around the buggy address:
0x0c087fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c087fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c087fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c087fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c087fff8000: fa fa fd fd fd fd fd fd fa fa 00 00 00 00 00 06
=>0x0c087fff8010: fa fa fd fd fd fd[fd]fd fa fa fa fa fa fa fa fa
0x0c087fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==31==ABORTING
在这里,我最初对您的代码所做的评论:
you keep a reference on an element of the vector, then you extend this vector, which can lead to reallocation. This reference becomes invalid
另一条评论 the link 解释了这种行为。
下面是一个最小的例子来明确说明相同的情况。
我们可以看到在扩展向量之后,整个存储在堆上的其他地方(元素已被移动)但引用仍然引用索引 2 处元素的先前地址。
/**
g++ -std=c++17 -o prog_cpp prog_cpp.cpp \
-pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
-g -O0 -UNDEBUG -fsanitize=address,undefined
$ ./prog_cpp
v from 0x602000000010 to 0x602000000020
elem at 0x602000000018 is 30
v from 0x603000000010 to 0x603000000024
elem at 0x602000000018=================================================================
==186890==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000018 at pc 0x55f3af7583e1 bp 0x7ffd22315d80 sp 0x7ffd22315d70
READ of size 4 at 0x602000000018 thread T0
...
**/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <iostream>
#include <vector>
int
main()
{
auto v=std::vector<int>{10, 20, 30, 40};
std::cout << "v from " << data(v) << " to " << data(v)+size(v) << '\n';
const auto &elem=v[2];
std::cout << "elem at " << &elem << std::flush << " is " << elem << '\n';
v.emplace_back(50);
std::cout << "v from " << data(v) << " to " << data(v)+size(v) << '\n';
std::cout << "elem at " << &elem << std::flush << " is " << elem << '\n';
return 0;
}
我遇到堆运行时错误。我怀疑它是过度使用 move 时的编译器错误。 代码的简化版本是
vector<vector<int>>result{{1,2,3}};
int size = result.size();
for(auto jdx = size-1; jdx >= 0; --jdx){
vector<int> &row = result[jdx];
// vector<int> newrow(row);
for(int idx = 2; idx >=1; --idx) {
vector<int> newrow(row);
result.push_back(newrow);
}
}
如果我将 newrow(row) 行换出最内层的循环,就可以像下面这样, 矢量<矢量>结果{{1,2,3}};
int size = result.size();
for(auto jdx = size-1; jdx >= 0; --jdx){
vector<int> &row = result[jdx];
vector<int> newrow(row);
for(int idx = 2; idx >=1; --idx) {
//vector<int> newrow(row);
result.push_back(newrow);
}
}
转储错误如下
=================================================================
==31==ERROR: AddressSanitizer: heap-use-after-free on address 0x6040000000b0 at pc 0x00000034789d bp 0x7ffd3434d6b0 sp 0x7ffd3434d6a8
READ of size 8 at 0x6040000000b0 thread T0
#4 0x7f9a26d790b2 (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
0x6040000000b0 is located 32 bytes inside of 48-byte region [0x604000000090,0x6040000000c0)
freed by thread T0 here:
#5 0x7f9a26d790b2 (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
previously allocated by thread T0 here:
#6 0x7f9a26d790b2 (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
Shadow bytes around the buggy address:
0x0c087fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c087fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c087fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c087fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c087fff8000: fa fa fd fd fd fd fd fd fa fa 00 00 00 00 00 06
=>0x0c087fff8010: fa fa fd fd fd fd[fd]fd fa fa fa fa fa fa fa fa
0x0c087fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==31==ABORTING
在这里,我最初对您的代码所做的评论:
you keep a reference on an element of the vector, then you extend this vector, which can lead to reallocation. This reference becomes invalid
另一条评论 the link 解释了这种行为。
下面是一个最小的例子来明确说明相同的情况。
我们可以看到在扩展向量之后,整个存储在堆上的其他地方(元素已被移动)但引用仍然引用索引 2 处元素的先前地址。
/**
g++ -std=c++17 -o prog_cpp prog_cpp.cpp \
-pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
-g -O0 -UNDEBUG -fsanitize=address,undefined
$ ./prog_cpp
v from 0x602000000010 to 0x602000000020
elem at 0x602000000018 is 30
v from 0x603000000010 to 0x603000000024
elem at 0x602000000018=================================================================
==186890==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000018 at pc 0x55f3af7583e1 bp 0x7ffd22315d80 sp 0x7ffd22315d70
READ of size 4 at 0x602000000018 thread T0
...
**/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <iostream>
#include <vector>
int
main()
{
auto v=std::vector<int>{10, 20, 30, 40};
std::cout << "v from " << data(v) << " to " << data(v)+size(v) << '\n';
const auto &elem=v[2];
std::cout << "elem at " << &elem << std::flush << " is " << elem << '\n';
v.emplace_back(50);
std::cout << "v from " << data(v) << " to " << data(v)+size(v) << '\n';
std::cout << "elem at " << &elem << std::flush << " is " << elem << '\n';
return 0;
}