У меня есть массив значений, который передается моей функции из другой части программы, которую мне нужно сохранить для последующей обработки. Поскольку я не знаю, сколько раз будет вызываться моя функция, прежде чем придет время обрабатывать данные, мне нужна динамическая структура хранения, поэтому я выбрал std::vector. Я не хочу делать стандартный цикл для push_back всех значений по отдельности, было бы неплохо, если бы я мог просто скопировать все это, используя что-то похожее на memcpy.
Как скопировать содержимое массива в std::vector на С++ без зацикливания?
Ответы (10)
Если вы можете построить вектор после того, как получили массив и размер массива, вы можете просто сказать:
std::vector<ValueType> vec(a, a + n);
... предполагая, что a - это ваш массив, а n - количество содержащихся в нем элементов. В противном случае std::copy() с resize() поможет.
Я бы держался подальше от memcpy(), если вы не можете быть уверены, что значения являются типами обычных данных (POD).
Кроме того, стоит отметить, что ни один из них на самом деле не избегает цикла for - это просто вопрос того, должны ли вы видеть его в своем коде или нет. Производительность O(n) во время выполнения неизбежна для копирования значений.
Наконец, обратите внимание, что массивы в стиле C являются вполне допустимыми контейнерами для большинства алгоритмов STL — необработанный указатель эквивалентен begin(), а (ptr + n) эквивалентен end().
vec.assign(a, a+n), что будет более компактным, чем копирование и изменение размера.
- person mMontu; 28.10.2013
Здесь было много ответов, и почти все они выполнят свою работу.
Однако есть несколько вводящих в заблуждение советов!
Вот варианты:
vector<int> dataVec;
int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
unsigned dataArraySize = sizeof(dataArray) / sizeof(int);
// Method 1: Copy the array to the vector using back_inserter.
{
copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}
// Method 2: Same as 1 but pre-extend the vector by the size of the array using reserve
{
dataVec.reserve(dataVec.size() + dataArraySize);
copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}
// Method 3: Memcpy
{
dataVec.resize(dataVec.size() + dataArraySize);
memcpy(&dataVec[dataVec.size() - dataArraySize], &dataArray[0], dataArraySize * sizeof(int));
}
// Method 4: vector::insert
{
dataVec.insert(dataVec.end(), &dataArray[0], &dataArray[dataArraySize]);
}
// Method 5: vector + vector
{
vector<int> dataVec2(&dataArray[0], &dataArray[dataArraySize]);
dataVec.insert(dataVec.end(), dataVec2.begin(), dataVec2.end());
}
Короче говоря, метод 4 с использованием vector::insert лучше всего подходит для сценария bsruth.
Вот некоторые кровавые подробности:
Способ 1, вероятно, проще всего понять. Просто скопируйте каждый элемент из массива и поместите его в конец вектора. Увы, медленно. Поскольку существует цикл (подразумеваемый функцией копирования), каждый элемент должен обрабатываться индивидуально; никакие улучшения производительности не могут быть сделаны на основе того факта, что мы знаем, что массив и векторы являются смежными блоками.
Метод 2 – это рекомендуемое улучшение производительности по сравнению с методом 1; просто предварительно зарезервируйте размер массива перед его добавлением. Для больших массивов это может помочь. Однако лучший совет здесь никогда не использовать резерв, если профилирование не предполагает, что вы можете получить улучшение (или вам нужно убедиться, что ваши итераторы не будут признаны недействительными). Бьерн согласен. Между прочим, я обнаружил, что этот метод работает медленнее большую часть времени, хотя я изо всех сил пытаюсь всесторонне объяснить, почему он регулярно значительно медленнее, чем метод 1...
Метод 3 — это решение старой школы — добавьте к проблеме немного C! Работает отлично и быстро для типов POD. В этом случае требуется вызов resize, так как memcpy работает за пределами вектора, и нет способа сообщить вектору, что его размер изменился. Помимо того, что это уродливое решение (копирование байтов!), помните, что это можно использовать только для типов POD. Я бы никогда не использовал это решение.
Способ 4 — лучший способ. Смысл понятен, он (обычно) самый быстрый и работает для любых объектов. Нет никаких недостатков в использовании этого метода для этого приложения.
Метод 5 является усовершенствованием метода 4: скопируйте массив в вектор, а затем добавьте его. Хороший вариант - в целом быстро и понятно.
Наконец, вы знаете, что вместо массивов можно использовать векторы, верно? Даже когда функция ожидает массивы в стиле c, вы можете использовать векторы:
vector<char> v(50); // Ensure there's enough space
strcpy(&v[0], "prefer vectors to c arrays");
Надеюсь, это поможет кому-то там!
&expr не оценивает expr, он только вычисляет его адрес. И указатель one за последним элементом тоже вполне корректен.
- person Roland Illig; 27.05.2011
ISO C++ forbids zero-size array ‘dataArray’ [-Wpedantic]
- person Ruslan; 21.12.2019
dataVec.insert(dataVec.end(), dataArray, dataArray + dataArraySize); — мне кажется намного понятнее. Не может ничего получить и от метода 5, только выглядит довольно неэффективным — если только компилятор не сможет снова оптимизировать вектор.
- person Aconcagua; 19.02.2020
std::unique_ptr + memcpy, потому что он сводится к memmove, которому предшествует несколько ветвей if.
- person Mikhail Vasilyev; 18.07.2021
Если все, что вы делаете, это заменяете существующие данные, то вы можете сделать это
std::vector<int> data; // evil global :)
void CopyData(int *newData, size_t count)
{
data.assign(newData, newData + count);
}
Поскольку я могу редактировать только свой собственный ответ, я собираюсь сделать составной ответ из других ответов на мой вопрос. Спасибо всем, кто ответил.
Используя std::copy, это все еще выполняется в фоновом режиме, но вы не не нужно вводить код.
int foo(int* data, int size)
{
static std::vector<int> my_data; //normally a class variable
std::copy(data, data + size, std::back_inserter(my_data));
return 0;
}
Используя обычный memcpy. Это, вероятно, лучше всего использовать для базовых типов данных (например, int), но не для более сложных массивов структур или классов.
vector<int> x(size);
memcpy(&x[0], source, size*sizeof(int));
std::copy), потому что дает больше гибкости, особенно если вы не хотите копировать весь вектор и можете добавить другой вектор.
- person Chef Pharaoh; 10.12.2019
избегайте memcpy, говорю я. Нет причин возиться с операциями с указателями, если вам это действительно не нужно. Кроме того, он будет работать только для типов POD (например, int), но не будет работать, если вы имеете дело с типами, требующими построения.
Еще один ответ, поскольку человек сказал: «Я не знаю, сколько раз будет вызываться моя функция», вы можете использовать метод вставки вектора, например, чтобы добавить массивы значений в конец вектора:
vector<int> x;
void AddValues(int* values, size_t size)
{
x.insert(x.end(), values, values+size);
}
Мне нравится этот способ, потому что реализация вектора должна оптимизироваться для наилучшего способа вставки значений на основе типа итератора и самого типа. Вы несколько отвечаете на реализацию stl.
Если вам нужно гарантировать максимальную скорость, и вы знаете, что ваш тип является типом POD, я бы рекомендовал метод изменения размера в ответе Томаса:
vector<int> x;
void AddValues(int* values, size_t size)
{
size_t old_size(x.size());
x.resize(old_size + size, 0);
memcpy(&x[old_size], values, size * sizeof(int));
}
В дополнение к методам, представленным выше, вам необходимо убедиться, что вы используете либо std::Vector.reserve(), std::Vector.resize(), либо создаете вектор по размеру, чтобы убедиться, что ваш вектор имеет достаточно элементов в это для хранения ваших данных. в противном случае вы испортите память. Это справедливо как для std::copy(), так и для memcpy().
Это причина использования vector.push_back(), вы не можете писать дальше конца вектора.
Предполагая, что вы знаете, насколько велик элемент в векторе:
std::vector<int> myArray;
myArray.resize (item_count, 0);
memcpy (&myArray.front(), source, item_count * sizeof(int));
http://www.cppreference.com/wiki/stl/vector/start
myints?
- person mavavilj; 02.08.2018