Рассмотрим следующую функцию
void removeOdd(vector<int>& v)
{
for(vector<int>::iterator it=v.begin(); it!=v.end(); )
{
if ((*it)%2) it = v.erase(it);
else it++;
}
}
который при ссылке на следующий тестовый скрипт
#include <vector>
#include <algorithm>
#include <iostream>
#include <cassert>
using namespace std;
void test()
{
int a[9] = { 5, 2, 8, 9, 6, 7, 3, 4, 1 };
vector<int> x(a, a+9); // construct x from the array
assert(x.size() == 9 && x.front() == 5 && x.back() == 1);
removeOdd(x);
assert(x.size() == 4);
sort(x.begin(), x.end());
int expect[4] = { 2, 4, 6, 8 };
for (int k = 0; k < 4; k++)
assert(x[k] == expect[k]);
}
int main()
{
test();
cout << "Passed" << endl;
}
и скомпилированный с помощью WSL g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 с флагом -fsanitize=address, работает нормально.
Команда компиляции: g++ -fsanitize=address test.cpp -o test
Вывод test: Passed
Однако следующий цикл while, аналог removeOdd,
void removeOdd_new(vector<int>& v)
{
for(vector<int>::iterator it=v.begin(); it!=v.end(); it++)
{
while ((*it)%2)
it = v.erase(it);
}
}
при компиляции тем же компилятором с теми же параметрами, т. е. g++ -fsanitize=address test_new.cpp -o test_new, при запуске выдается следующая ошибка
==207338==ERROR: AddressSanitizer: negative-size-param: (size=-4)
#0 0x7f26ba2d5f97 in __interceptor_memmove ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:810
#1 0x5577715186c7 in int* std::__copy_move<true, true, std::random_access_iterator_tag>::__copy_m<int>(int const*, int const*, int*) (/mnt/c/Users/somebody/Downloads/test_new+0x86c7)
#2 0x557771517a2b in int* std::__copy_move_a2<true, int*, int*>(int*, int*, int*) (/mnt/c/Users/somebody/Downloads/test_new+0x7a2b)
#3 0x5577715166e0 in int* std::__copy_move_a1<true, int*, int*>(int*, int*, int*) (/mnt/c/Users/somebody/Downloads/test_new+0x66e0)
#4 0x5577715157fb in __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::__copy_move_a<true, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >) (/mnt/c/Users/somebody/Downloads/test_new+0x57fb)
#5 0x557771514e37 in __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::move<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >) (/mnt/c/Users/somebody/Downloads/test_new+0x4e37)
#6 0x5577715144bd in std::vector<int, std::allocator<int> >::_M_erase(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >) (/mnt/c/Users/somebody/Downloads/test_new+0x44bd)
#7 0x557771513792 in std::vector<int, std::allocator<int> >::erase(__gnu_cxx::__normal_iterator<int const*, std::vector<int, std::allocator<int> > >) (/mnt/c/Users/somebody/Downloads/test_new+0x3792)
#8 0x5577715125ec in removeOdd_new(std::vector<int, std::allocator<int> >&) (/mnt/c/Users/somebody/Downloads/test_new+0x25ec)
#9 0x557771512b91 in test() (/mnt/c/Users/somebody/Downloads/test_new+0x2b91)
#10 0x557771512f44 in main (/mnt/c/Users/somebody/Downloads/test_new+0x2f44)
#11 0x7f26b9d69d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#12 0x7f26b9d69e3f in __libc_start_main_impl ../csu/libc-start.c:392
#13 0x5577715123c4 in _start (/mnt/c/Users/somebody/Downloads/test_new+0x23c4)
0x604000000024 is located 20 bytes inside of 36-byte region [0x604000000010,0x604000000034)
allocated by thread T0 here:
#0 0x7f26ba3521e7 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x5577715168f7 in __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) (/mnt/c/Users/somebody/Downloads/test_new+0x68f7)
#2 0x557771515b79 in std::allocator_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) (/mnt/c/Users/somebody/Downloads/test_new+0x5b79)
#3 0x5577715150fb in std::_Vector_base<int, std::allocator<int> >::_M_allocate(unsigned long) (/mnt/c/Users/somebody/Downloads/test_new+0x50fb)
#4 0x557771514824 in void std::vector<int, std::allocator<int> >::_M_range_initialize<int*>(int*, int*, std::forward_iterator_tag) (/mnt/c/Users/somebody/Downloads/test_new+0x4824)
#5 0x557771513950 in std::vector<int, std::allocator<int> >::vector<int*, void>(int*, int*, std::allocator<int> const&) (/mnt/c/Users/somebody/Downloads/test_new+0x3950)
#6 0x557771512a79 in test() (/mnt/c/Users/somebody/Downloads/test_new+0x2a79)
#7 0x557771512f44 in main (/mnt/c/Users/somebody/Downloads/test_new+0x2f44)
#8 0x7f26b9d69d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
SUMMARY: AddressSanitizer: negative-size-param ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:810 in __interceptor_memmove
==207338==ABORTING
Я не могу понять причину, по которой версия цикла while выдает ошибку адреса, поскольку цикл while логически ничем не отличается от цикла for.
Я также понимаю, что возвращаемое значение std::vector.erase() - это итератор (a)n, указывающий на новое местоположение элемента, следующего за последним элементом, стертым вызовом функции. Это конец контейнера, если операция удалила последний элемент в последовательности. ." Эта документация взята с сайта https://cplusplus.com/reference/vector/vector/erase/.
Таким образом, переменная it в цикле while должна соответствующим образом обновиться (но выдается ошибка). Я также был бы признателен, если бы кто-нибудь с хорошим пониманием внутреннего устройства cpp помог интерпретировать сообщения об ошибках компилятора. Спасибо!
Ссылка на test.cpp и test_new.cpp: https://hackmd.io/54Mm2B1pStSPTWOkXJ4QtQ
К вашему сведению -- void removeOdd(vector<int>& v) { v.erase(std::remove_if (v.begin(), v.end(), [](int n) { return n%2; }), v.end()); }
@PaulMcKenzie А конкретно для векторов теперь есть std::erase().
Рекомендация: прочтите «Отладка резиновой утки».
Эти две функции не эквивалентны.
Assert потенциально невозможен
Эти две функции не эквивалентны. Функция removeOdd_new() будет увеличивать it после вызова erase() (т. е. всякий раз, когда элемент удаляется). Исходный removeOdd() не увеличивается it после вызова erase().





Эти два понятия не эквивалентны.
Этот:
void removeOdd(vector<int>& v) { for(vector<int>::iterator it=v.begin(); it!=v.end(); ) { if ((*it)%2) it = v.erase(it); else it++; } }
Будет перебирать все элементы вектора. Он увеличивает it до тех пор, пока it==v.end(), а затем функция возвращается.
С другой стороны, здесь
void removeOdd_new(vector<int>& v) { for(vector<int>::iterator it=v.begin(); it!=v.end(); it++) { while ((*it)%2) it = v.erase(it); } }
Как только it достигнет последнего элемента, если этот элемент odd, он будет удален, it станет v.end() и *it разыменует конечный итератор, который не определен.
Once it reaches the last element, if that element is odd it will be erased, it becomes v.end() затем выполняется it++, пропуская итератор на один конец, в результате чего сравнение it != v.end() не работает должным образом, и вы повторно входите в цикл и происходит сбой при попытке отменить ссылку на итератор.
@MartinYork цикл while имеет неопределенное поведение. После этого все ставки сняты.
от: почему ты вот так смешиваешь c-массивы и векторы? Либо вы используете древний компилятор, либо можете просто написать
vector<int> x{ 5, 2, 8, 9, 6, 7, 3, 4, 1 };