Гарантируется ли непрерывность элементов std :: vector?

Мой вопрос прост: гарантированно ли элементы std :: vector будут смежными? В порядке слова, могу ли я использовать указатель на первый элемент std :: vector как C-массив?

Если мне не изменяет память, стандарт C ++ не давал такой гарантии. Однако требования std :: vector были таковы, что их было практически невозможно удовлетворить, если элементы не были смежными.

Может кто-нибудь прояснить это?

Пример:

std::vector<int> values;
// ... fill up values

if( !values.empty() )
{
    int *array = &values[0];
    for( int i = 0; i < values.size(); ++i )
    {
        int v = array[i];
        // do something with 'v'
    }
}

person Martin Cote    schedule 11.05.2009    source источник
comment
Я знаю, что у вас проблемы, если вы измените values внутри этого if блока. Однако я не знаю ответа на ваш вопрос, поэтому просто оставляю комментарий. :)   -  person Greg D    schedule 11.05.2009
comment
@Greg: Что за проблема - не могли бы вы немного уточнить?   -  person Reunanen    schedule 11.05.2009
comment
Я полагаю, он имел в виду, что добавление новых значений может вызвать перераспределение, которое приведет к тому, что массив станет недействительным.   -  person Martin Cote    schedule 11.05.2009
comment
Вызовы, которые изменяют values, в частности, изменяют его размер (например, push_back()), могут вызывать перераспределение базового вектора, что делает недействительным указатель, скопированный в array. Это тот же принцип, что и при использовании vector :: iterator вместо указателя на вектор. :)   -  person Greg D    schedule 11.05.2009
comment
В порядке. Я читал это, если вы измените значения, то есть присвоите элементам. Я считаю, что это никогда не должно вызывать никаких проблем.   -  person Reunanen    schedule 11.05.2009
comment
Да, я поставил символы вокруг значений, чтобы прояснить, что я говорю о самом классе, а не о значениях, содержащихся в нем. :) Неудачное название и все такое. Я не думаю, что это действительно проблема в общем случае, когда этот вопрос актуален - зачем кому-то хватать указатель на память, а затем начинать гадать с вектором вместо использования указателя? Глупость.   -  person Greg D    schedule 11.05.2009


Ответы (7)


Это было пропущено в самом стандарте C ++ 98, но позже добавлено как часть TR. Грядущий стандарт C ++ 0x, конечно же, будет содержать это как требование.

Из n2798 (черновик C ++ 0x):

23.2.6 Вектор шаблона класса [вектор]

1 Вектор - это контейнер последовательности, который поддерживает итераторы произвольного доступа. Кроме того, он поддерживает (амортизированные) операции вставки и стирания с постоянным временем в конце; вставка и стирание в середине занимают линейное время. Управление хранилищем осуществляется автоматически, хотя могут быть даны подсказки для повышения эффективности. Элементы вектора хранятся непрерывно, что означает, что если v - вектор, где T - некоторый тип, отличный от bool, то он подчиняется тождеству & v [n] == & v [0] + n для всех 0 ‹= n‹ v .размер().

person dirkgently    schedule 11.05.2009
comment
Это также указано в ISO 14882, 2-е издание: раздел 23.2.4 [lib.vector]: элементы вектора хранятся непрерывно, что означает, что если v - вектор ‹T, Allocator›, где T - некоторый тип, отличный от bool , то он подчиняется тождеству & v [n] == & v [0] + n для всех 0 ‹= n‹ v.size (). - person Mike Caron; 11.05.2009
comment
так s, TR, TC, :) На самом деле C ++ 03 также называется C ++ 98-TC1 (техническое исправление) из того, что я читал - person Johannes Schaub - litb; 11.05.2009
comment
@litb: Правильно. Я все время забываю, что есть что. - person dirkgently; 11.05.2009
comment
А как насчет векторов векторов? Внутренние векторы сразу после внутренних векторов последней группы? - person huseyin tugrul buyukisik; 04.04.2016
comment
Как bool особенный как T в векторе? Согласно. Элементы вектора хранятся непрерывно, что означает, что если v - это вектор, где T - это какой-то тип, отличный от bool. - person Thomson; 15.10.2016
comment
@huseyin tugrul buyukisik вы узнали ответ на этот вопрос? Мне также интересно, как это работает - person David Doria; 28.10.2016
comment
@huseyin tugrul buyukisik Это, конечно, правда, но экземпляры последующихstd::vector являются смежными. Например: instd::vector<std::vector<int>> v элементы v[0], v[1], ... впоследствии сохраняются в памяти, но элементы v[0].back() и v[1].front() не гарантируются. - person jarzec; 24.11.2017
comment
@Thomson std::vector<bool> может быть оптимизирован для пространства, используя только (примерно) один бит вместо целого байта. Лучшая справочная информация, которую я смог быстро найти, была следующая: en.cppreference.com/w/cpp / container / vector_bool - person jarzec; 24.11.2017

Как указывали другие ответы, содержимое вектора гарантированно будет непрерывным (за исключением странности bool).

Комментарий, который я хотел добавить, заключается в том, что если вы выполняете вставку или удаление вектора, что может привести к перераспределению памяти вектором, тогда вы сделаете недействительными все ваши сохраненные указатели и итераторы.

person Bill Lynch    schedule 11.05.2009
comment
Элементы все равно будут храниться в непрерывном блоке памяти, просто в другом месте. Вопрос был конкретно о смежности. - person Dima; 11.05.2009
comment
Но существующие указатели и итераторы станут недействительными. - person Bill Lynch; 11.05.2009
comment
Хорошая точка зрения. Вы должны вставить это в свой ответ, чтобы прояснить, что вы имеете в виду. - person Dima; 14.05.2009
comment
Теперь я знаю, почему в моей программе вчера произошел сбой, когда я перебирал ее в двойном цикле, удаляя определенные элементы :) Спасибо! - person user2891462; 30.07.2015
comment
@ user2891462: stackoverflow .com / questions / 7958216 / - person Bill Lynch; 31.07.2015
comment
@BillLynch Спасибо! Это то, к чему я в конечном итоге стремился, но я не мог понять, в чем был этот segfault. - person user2891462; 31.07.2015
comment
Спасибо, прошивка испортила память, есть мой segfault от: p - person Romain Laneuville; 25.02.2020

Стандарт действительно гарантирует, что vector является непрерывным в памяти и что &a[0] может быть передано C функции, которая ожидает массив.

Исключением из этого правила является vector<bool>, который использует только один бит на bool, поэтому, хотя у него есть непрерывная память, его нельзя использовать как bool* (это широко считается ложной оптимизацией и ошибкой).

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

person Motti    schedule 11.05.2009
comment
›Кстати, почему бы вам не использовать итераторы? Вот для чего они нужны. Возможно, он читал новую статью Алексанреску по этой теме: boostcon.com/site-media/var/sphene/sphwiki/attachment/2009/05/ - person Nemanja Trifunovic; 11.05.2009
comment
Спасибо за ссылку, перейду в свой список чтения (я стараюсь не пропускать статьи Александресу) - person Motti; 11.05.2009
comment
Мвахаха, кажется, сейчас все говорят об этой презентации. Послушайте, обсуждение этого все еще продолжается: группы . google.com/group/comp.lang.c++.moderated/browse_thread/ - person Johannes Schaub - litb; 11.05.2009
comment
Если вы внимательно ее прочитаете, статья Александреску на самом деле не говорит «Не использовать итераторы в C ++», в ней говорится «Проверьте D. Подход, который он описывает в этой статье, поразительно похож на любые существующие языки и фреймворки, вобравшие в себя функциональное наследие ( List, Scheme, Haskell), и я серьезно сомневаюсь, что еще один синтаксис на основе C является идеальной отправной точкой для лучшей обработки списков. Некоторое время назад в прошлом году я кратко пытался убедить его направить свои значительные таланты на улучшение уже устоявшегося языка, такого как C #, но, боюсь, безуспешно! :) - person Daniel Earwicker; 18.02.2010

Как уже говорили другие, vector внутренне использует непрерывный массив объектов. Указатели на этот массив должны рассматриваться как недопустимые, если любая неконстантная функция-член вызывается IIRC.

Однако есть исключение !!

vector<bool> имеет специальную реализацию, предназначенную для экономии места, так что каждый логический элемент использует только один бит. Базовый массив не является непрерывным массивом bool, и арифметика массива на vector<bool> работает не так, как vector<T>.

(Я полагаю, также возможно, что это может быть верно для любой специализации вектора, поскольку мы всегда можем реализовать новую. Однако std::vector<bool> - единственная, ошибочная, стандартная специализация, на которой простая арифметика указателей не работает.)

person Wuggy    schedule 12.05.2009
comment
Пользователю не разрешено специализироваться std::vector, а все другие векторы должны использовать непрерывное хранилище. Следовательно, std::vector<bool> (к счастью) единственный странный стандартный вектор. (Я твердо убежден, что эту специализацию следует исключить и заменить, например, на std::dynamic_bitset с почти такой же функциональностью. Это неплохая структура данных, это просто не вектор.) - person Arne Vogel; 22.09.2017

Я нашел этот поток, потому что у меня есть вариант использования, когда векторы, использующие непрерывную память, являются преимуществом.

Я учусь использовать объекты буфера вершин в OpenGL. Я создал класс-оболочку, содержащий логику буфера, поэтому все, что мне нужно сделать, это передать массив чисел с плавающей запятой и несколько значений конфигурации для создания буфера. Я хочу иметь возможность сгенерировать буфер из функции на основе пользовательского ввода, поэтому длина не известна во время компиляции. Самым простым решением было бы сделать что-то подобное:

void generate(std::vector<float> v)
{
  float f = generate_next_float();
  v.push_back(f);
}

Теперь я могу передать векторные числа с плавающей запятой в виде массива в функции, связанные с буфером OpenGL. Это также устраняет необходимость в sizeof для определения длины массива.

Это намного лучше, чем выделить огромный массив для хранения чисел с плавающей запятой и надеяться, что я сделал его достаточно большим, или создать свой собственный динамический массив с непрерывным хранилищем.

person NobodyImportant    schedule 08.12.2012
comment
эта функция не имеет для меня никакого смысла. вы хотите передать ссылку или указатель на v, а не на сам v? потому что передача только v вызовет создание копии внутри функции, которая перестанет существовать после завершения функции. Таким образом, вы нажимаете что-то на вектор только для того, чтобы удалить вектор, когда функция завершится. - person johnbakers; 05.04.2013

cplusplus.com:

Векторные контейнеры реализованы в виде динамических массивов; Как и в обычных массивах, элементы векторных контейнеров хранятся в смежных местах хранения, а это означает, что к их элементам можно получить доступ не только с помощью итераторов, но и с помощью смещений в обычных указателях на элементы.

person Igor Oks    schedule 11.05.2009

Да, элементы std :: vector гарантированно будут смежными.

person Benoît    schedule 11.05.2009