vector::reserve() and resize()
От: niXman Ниоткуда https://github.com/niXman
Дата: 07.05.20 08:08
Оценка: :)
привет!

столкнулся с такой странностью.
vector<char> cv;
cv.reserve(4);
char *beg = cv.data(); // beg валиден
// работаем с beg ...
cv.resize(2); // beg становится невалидным


почему вектор выполняет реаллокацию даже в том случае, когда capacity() больше чем size?

спасибо.
Слово "совпадение" появилось после падения сов.
Re: vector::reserve() and resize()
От: Stanislav V. Zudin Россия  
Дата: 07.05.20 08:18
Оценка: 9 (1) +2
Здравствуйте, niXman, Вы писали:

X>привет!


X>столкнулся с такой странностью.

X>
X>vector<char> cv;
X>cv.reserve(4);
X>char *beg = cv.data(); // beg валиден
X>// работаем с beg ...
X>cv.resize(2); // beg становится невалидным
X>


X>почему вектор выполняет реаллокацию даже в том случае, когда capacity() больше чем size?


А как выяснилось, что невалиден?
vector<char> cv;
cv.reserve(4);
char *beg = cv.data(); // beg валиден
cv.resize(2); // beg становится невалидным
char *beg2 = cv.data(); // beg валиден

if (beg != beg2)
{
   // Попадаем сюда?
}


Это где такое? ОСь, версия stl?
_____________________
С уважением,
Stanislav V. Zudin
Re[2]: vector::reserve() and resize()
От: niXman Ниоткуда https://github.com/niXman
Дата: 07.05.20 08:22
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>А как выяснилось, что невалиден?

ну...данные затираются.
я как-то не подумал что возможно внутрях может выполняеться что-то типа memset()... ща проверю!

SVZ>Это где такое? ОСь, версия stl?

ubuntu-18.04, GCC-8.4.0
Слово "совпадение" появилось после падения сов.
Re[2]: vector::reserve() and resize()
От: niXman Ниоткуда https://github.com/niXman
Дата: 07.05.20 08:28
Оценка: :)
Здравствуйте, Stanislav V. Zudin, Вы писали:

да, ты прав!
адрес не изменяется, но данные затираются! я ошибся...

в любом случае такое поведение мне кажется ошибочным...
Слово "совпадение" появилось после падения сов.
Re[2]: vector::reserve() and resize()
От: niXman Ниоткуда https://github.com/niXman
Дата: 07.05.20 08:32
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

да, в векторе выполняется инициализация каждого элемента:
  template<typename _T1, typename... _Args>
    inline void
    _Construct(_T1* __p, _Args&&... __args)
    { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
Слово "совпадение" появилось после падения сов.
Re[3]: vector::reserve() and resize()
От: watchmaker  
Дата: 07.05.20 08:35
Оценка: +1
Здравствуйте, niXman, Вы писали:


X>адрес не изменяется

В общем-то это не случайность и не прихоть реализации. Он гарантированно не будет меняться, так как resize делается на размер не превышающий capacity().

X>но данные затираются!

Какие данные? В твоём примере вектор пуст до момента вызова resize. Как может затереться то, чего нет?

X>в любом случае такое поведение мне кажется ошибочным...

Какое поведение?
Re[3]: vector::reserve() and resize()
От: night beast СССР  
Дата: 07.05.20 08:35
Оценка: +3
Здравствуйте, niXman, Вы писали:

X>в любом случае такое поведение мне кажется ошибочным...


reserve просто резервирует память, без мемсета.
resize инициализирует недостающее количество переданным значением, или деструктит лишнее
Re[4]: vector::reserve() and resize()
От: niXman Ниоткуда https://github.com/niXman
Дата: 07.05.20 08:50
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Какие данные? В твоём примере вектор пуст до момента вызова resize. Как может затереться то, чего нет?


ну какбы да, формально вектор пуст потому что в момент его использования size() вернет 0
Слово "совпадение" появилось после падения сов.
Re: vector::reserve() and resize()
От: _NN_ www.nemerleweb.com
Дата: 07.05.20 08:54
Оценка:
Здравствуйте, niXman, Вы писали:

X>привет!


X>столкнулся с такой странностью.

X>
X>vector<char> cv;
X>cv.reserve(4);
X>char *beg = cv.data(); // beg валиден
X>// работаем с beg ...
X>cv.resize(2); // beg становится невалидным
X>


Закрался вопрос, а как именно работаем с beg ?
Единственное, что мы получаем это указатель, который нельзя разыменовывать.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: vector::reserve() and resize()
От: niXman Ниоткуда https://github.com/niXman
Дата: 07.05.20 09:21
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Закрался вопрос, а как именно работаем с beg ?

кладем в него данные, т.е. разыменовываем.

_NN>Единственное, что мы получаем это указатель, который нельзя разыменовывать.

почему нельзя? reserve() ведь выделяет память, а data() возвращает указатель на выделенную память.
что не так?
Слово "совпадение" появилось после падения сов.
Re[3]: vector::reserve() and resize()
От: Stanislav V. Zudin Россия  
Дата: 07.05.20 09:33
Оценка:
Здравствуйте, niXman, Вы писали:

X>почему нельзя? reserve() ведь выделяет память, а data() возвращает указатель на выделенную память.

X>что не так?

Не так тут то, что reserve() _может_ аллоцировать память, но не добавляет элементы в массив.
Сперва нужно положить элементы в массив, а потом уже работать с ними.

ИМХО зря в вектор добавили метод data().
&vec[0] — законный, валидный способ достучаться до памяти.
_____________________
С уважением,
Stanislav V. Zudin
Re[4]: vector::reserve() and resize()
От: niXman Ниоткуда https://github.com/niXman
Дата: 07.05.20 09:36
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>Не так тут то, что reserve() _может_ аллоцировать память, но не добавляет элементы в массив.


это именно то, что мне и нужно.
мне не нужен push_back() или подобные.

но так как reserve() аллоцирует память, и так как я могу получить ее используя 'operator[]' или 'data()' — я не вижу тут притиворечий.
даже в доке я ничего не нашел по моему случаю.

SVZ>ИМХО зря в вектор добавили метод data().

SVZ>&vec[0] — законный, валидный способ достучаться до памяти.

так а 'data()' чем мешает?
Слово "совпадение" появилось после падения сов.
Re[3]: vector::reserve() and resize()
От: _NN_ www.nemerleweb.com
Дата: 07.05.20 09:43
Оценка:
Здравствуйте, niXman, Вы писали:

X>Здравствуйте, _NN_, Вы писали:


_NN>>Закрался вопрос, а как именно работаем с beg ?

X>кладем в него данные, т.е. разыменовываем.

_NN>>Единственное, что мы получаем это указатель, который нельзя разыменовывать.

X>почему нельзя? reserve() ведь выделяет память, а data() возвращает указатель на выделенную память.
X>что не так?

Потому что это не так.
Память то выделяем, но указатель из data может не указывать на эту область.

https://en.cppreference.com/w/cpp/container/vector/data

Returns pointer to the underlying array serving as element storage. The pointer is such that range [data(); data() + size()) is always a valid range, even if the container is empty (data() is not dereferenceable in that case).


Валидным будет только полуинтервал data() — data + size().
size() у нас 0, потому что мы только зарезервировали, но не изменили размер.
Получаем полуинтервал [ data() ; data ), т.е. разыменовывать нельзя.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[5]: vector::reserve() and resize()
От: Stanislav V. Zudin Россия  
Дата: 07.05.20 09:43
Оценка:
Здравствуйте, niXman, Вы писали:

SVZ>>Не так тут то, что reserve() _может_ аллоцировать память, но не добавляет элементы в массив.


X>это именно то, что мне и нужно.

X>мне не нужен push_back() или подобные.

Ну у вектора определенная семантика и твой юзкейс явно в неё не вписывается.

X>но так как reserve() аллоцирует память, и так как я могу получить ее используя 'operator[]' или 'data()' — я не вижу тут притиворечий.

X>даже в доке я ничего не нашел по моему случаю.

SVZ>>ИМХО зря в вектор добавили метод data().

SVZ>>&vec[0] — законный, валидный способ достучаться до памяти.

X>так а 'data()' чем мешает?


Позволяет использовать вектор не по назначению, как в твоём примере.

Мне просто не очень понятно, какую задачу ты решаешь.
Если нужен "буфер + RAII", то передавай в к-тор число элементов.
Ну или вместо reserve дергай resize().
_____________________
С уважением,
Stanislav V. Zudin
Re[4]: vector::reserve() and resize()
От: niXman Ниоткуда https://github.com/niXman
Дата: 07.05.20 09:47
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Потому что это не так.

_NN>Память то выделяем, но указатель из data может не указывать на эту область.

_NN>https://en.cppreference.com/w/cpp/container/vector/data

_NN>

_NN>Returns pointer to the underlying array serving as element storage. The pointer is such that range [data(); data() + size()) is always a valid range, even if the container is empty (data() is not dereferenceable in that case).


_NN>Валидным будет только полуинтервал data() — data + size().

_NN>size() у нас 0, потому что мы только зарезервировали, но не изменили размер.
_NN>Получаем полуинтервал [ data() ; data ), т.е. разыменовывать нельзя.

с формальной точки зрения ты прав, да.

но с практической — как бы нет =)
Слово "совпадение" появилось после падения сов.
Re[6]: vector::reserve() and resize()
От: niXman Ниоткуда https://github.com/niXman
Дата: 07.05.20 09:48
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>Позволяет использовать вектор не по назначению, как в твоём примере.


в моем юзкейсе я сначала зову reserve(), и потом data().
разве это не эквивалентно reserve() и '&vec[0]' ?
Слово "совпадение" появилось после падения сов.
Re[7]: vector::reserve() and resize()
От: niXman Ниоткуда https://github.com/niXman
Дата: 07.05.20 09:50
Оценка:
Здравствуйте, niXman, Вы писали:

X>разве это не эквивалентно reserve() и '&vec[0]' ?


и да, я понимаю что для пустого вектора 'operator[]' приведет к SEGFLT, а data() — нет.
Слово "совпадение" появилось после падения сов.
Отредактировано 07.05.2020 10:04 niXman . Предыдущая версия .
Re[5]: vector::reserve() and resize()
От: _NN_ www.nemerleweb.com
Дата: 07.05.20 09:50
Оценка:
Здравствуйте, niXman, Вы писали:

X>с формальной точки зрения ты прав, да.


X>но с практической — как бы нет =)


С практической точки зрения можно делать и const_cast<char*>(myStdString.c_str()) , но мы тогда закладываемся на определённую реалзиацию.
Если нужно именно такое поведение от вектора, то нужно в кода указать требования компилятора:
#if COMPILER IS GCC AND VERSION == 1.2.3.4 AND PLATFORM = x86 ...
vec.resere(...)
*vec.data() = 1;
#else
#error Must use only supported compiler
#endif


А самый главный вопрос зачем это нужно ?
Если нужно просто выделить память и использовать то простейший вариант make_unique<int[]>(10);
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[4]: vector::reserve() and resize()
От: watchmaker  
Дата: 07.05.20 09:51
Оценка: 4 (1) +1
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>зря в вектор добавили метод data().

SVZ>&vec[0] — законный, валидный способ достучаться до памяти.
Пара причин:
Во втором случае это позволяет значительно удобнее передавать содержимое вектора во другие функции:
template <class T>
void print_array(const T a[], size_t length);


std::vector<T> vec = ...;

print_array(vec.data(), vec.size()); // ок
print_array(vec.empty() ? nullptr : std::addressof(vec[0]), vec.size()); // ок, но выглядит хреново
print_array(&vec[0], vec.size()); // багло
Re[6]: vector::reserve() and resize()
От: niXman Ниоткуда https://github.com/niXman
Дата: 07.05.20 09:57
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Если нужно именно такое поведение от вектора, то нужно в кода указать требования компилятора:

_NN>
_NN>#if COMPILER IS GCC AND VERSION == 1.2.3.4 AND PLATFORM = x86 ...
_NN>vec.resere(...)
_NN>*vec.data() = 1;
_NN>#else
_NN>#error Must use only supported compiler
_NN>#endif
_NN>


я не вижу в доках по reserve() никаких слов о том, что он может НЕ аллоцировать память в случае когда ты зовешь reserve() для пустого вектора и аргументом указано значение отличное от нуля.


_NN>А самый главный вопрос зачем это нужно ?

_NN>Если нужно просто выделить память и использовать то простейший вариант make_unique<int[]>(10);

да, нужно просто выделить кусок памяти. с вектором казалось проще.
Слово "совпадение" появилось после падения сов.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.