std :: copy и std :: vector задача - PullRequest
       8

std :: copy и std :: vector задача

5 голосов
/ 10 ноября 2010

Я понимаю, почему это вызывает segfault:

#include <algorithm>
#include <vector>
using namespace std;

int main()
{
    vector<int> v;
    int iArr[5] = {1, 2, 3, 4, 5};
    int *p = iArr;

    copy(p, p+5, v.begin());

    return 0;
}

Но почему это не вызывает segfault?

#include <algorithm>
#include <vector>
using namespace std;

int main()
{
    vector<int> v;
    int iArr[5] = {1, 2, 3, 4, 5};
    int *p = iArr;

    v.reserve(1);
    copy(p, p+5, v.begin());

    return 0;
}

Ответы [ 7 ]

7 голосов
/ 10 ноября 2010

И то, и другое неправильно, поскольку вы копируете в пустой вектор, а для копирования требуется место для вставки. Он не изменяет размер контейнера сам по себе. Здесь вам, вероятно, понадобятся back_insert_iterator и back_inserter:

copy(p, p+5, back_inserter(v));
7 голосов
/ 10 ноября 2010

Это неопределенное поведение - reserve() выделяет буфер для как минимум одного элемента, а элемент остается неинициализированным.

Таким образом, либо буфер достаточно велик, и вы можете технически это сделатьполучить доступ к элементам за пределами первого или он недостаточно велик, и вы просто не заметили никаких проблем.

Суть в том - не делайте этого.Доступ только к элементам, которые законно хранятся в экземпляре vector.

2 голосов
/ 10 ноября 2010

Но почему это не приводит к segfault?

Потому что звезды выровнены.Или вы работали в режиме отладки, и компилятор сделал что-то, чтобы «помочь» вам.Суть в том, что вы делаете неправильную вещь и перешли в темный и недетерминированный мир неопределенного поведения.Вы reserve одна точка в векторе, а затем пытаетесь втиснуть 5 элементов в reserve -области.Плохо.

У вас есть 3 варианта.В моем личном порядке предпочтений:

1) Используйте back_insert_iterator, который предназначен именно для этой цели.Предоставлено #include <iterator>.Синтаксис немного причудливый, но, к счастью, есть хороший ярлык с сахарным покрытием, back_inserter также предоставляется:

#include <iterator>
// ...
copy( p, p+5, back_inserter(v) );

2) assign элементы вектора.Я предпочитаю этот метод чуть менее просто, потому что assign является членом vector, и это кажется мне несколько менее общим, чем использование somethign из algorithm.

v.assign(p, p+5);

3) reserveправильное количество элементов, затем скопируйте их.Я считаю, что это последний шаг на случай, если все остальное не получится по какой-либо причине.Он основан на том факте, что хранилище vector является смежным, поэтому оно не является универсальным, и это просто похоже на секретный метод передачи данных в vector.

2 голосов
/ 10 ноября 2010

Это неправильно! это неопределенное поведение для доступа к памяти, которой вы не владеете, даже если это работает в примере. Думаю, причина в том, что std::vector резервирует более одного элемента.

0 голосов
/ 10 ноября 2010

Между прочим, было бы неправильно даже копировать 1 элемент в вектор таким образом (или резервировать 5, а затем копировать таким образом).

Причина, по которой он, скорее всего, не является сигфо, состоит в том, чтоРазработчик посчитал, что было бы неэффективно выделять память только для 1 элемента на тот случай, если вы захотите увеличить его позже, поэтому, возможно, их было выделено достаточно для 16 или 32 элементов.Прямые 5 элементов, вероятно, не были бы неопределенным поведением, но были бы неправильными, потому что вектор не будет иметь логический размер 5, а копия будет почти «потрачена впустую», поскольку вектор будет утверждать, что все еще имеет размер 0.

Правильным поведением будет резерв (5), вставьте элемент, сохраните где-нибудь его итератор, вставьте еще 4 элемента и посмотрите на содержимое первого итератора.Reserve () гарантирует, что итераторы не станут недействительными до тех пор, пока вектор не превысит этот размер, или пока не будет выполнен вызов, такой как erase (), clear (), resize () или другой резерв ().

0 голосов
/ 10 ноября 2010

Скорее всего, потому, что пустому вектору вообще не выделена память, поэтому вы пытаетесь записать указатель NULL, что обычно приводит к мгновенному сбою. Во втором случае ему выделена, по крайней мере, некоторая память, и вы, скорее всего, перезаписываете конец массива, что может или не может привести к падению в C ++.

Оба не правы.

0 голосов
/ 10 ноября 2010

Потому что тебе не повезло. Доступ к памяти не выделен UB.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...