2010-11-10 34 views
5

rozumiem dlaczego ta powoduje segfault:std :: copy i std :: vector problemem

#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; 
} 

Ale dlaczego nie to spowodować 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; 
} 
+0

@KennyTM druga przestrzeń rezerwowa dla 1 elementu –

+0

Istnieje dobra odpowiedź związana z różnymi rodzajami niezdefiniowanego zachowania w C++: http://stackoverflow.com/questions/367633/what-are-all-the-common- undefined-behaviour-that-ac-programmer-should-know-abo/367662 # 367662 –

+7

Dlaczego po prostu nie użyjesz 'copy (p, p + 5, back_inserter (v));' i unikniesz całego tego bałaganu? –

Odpowiedz

7

To jest niezdefiniowane zachowanie - reserve() przydziela bufor dla co najmniej jeden element i element pozostaje niezainicjowany.

Tak więc bufor jest wystarczająco duży, więc technicznie można uzyskać dostęp do elementów poza pierwszym lub nie jest wystarczająco duży i po prostu nie zauważysz żadnych problemów.

Najważniejsze jest - nie rób tego. Tylko elementy dostępu, które są legalnie przechowywane w instancji vector.

+0

Wolałabym również wywoływać 'resize()' over 'reserve()' przed skopiowaniem elementów - w przeciwnym razie nie można iterować elementów i 'size()' nadal będzie zwracało 0, a następne 'push_back()' będzie zacznij nadpisywać to, co skopiowałeś. – AshleysBrain

+0

Mam to. Powinienem 'resize()' zamiast 'reserve()'. Jaka jest różnica między tymi dwoma? – nakiya

+0

@nakiya: Klucz, o który musisz uprzejmie poprosić 'vector', aby przydzielił wystarczająco duży bufor, w przeciwnym razie jest to UB. – sharptooth

0

Ponieważ miałeś pecha. Brak dostępu do pamięci to UB.

2

To jest złe! nieokreślonym zachowaniem jest dostęp do pamięci, która nie jest Twoją własnością, nawet jeśli działa w przykładzie. Myślę, że powodem jest to, że std::vector zarezerwuje więcej niż jeden element.

0

Najprawdopodobniej dlatego, że pusty wektor nie ma w ogóle przydzielonej pamięci, więc próbujesz pisać do wskaźnika NULL, który zwykle prowadzi do natychmiastowej awarii. W drugim przypadku przydzielona jest przynajmniej część pamięci, a najprawdopodobniej nadpisujesz koniec tablicy, co może prowadzić do awarii w C++.

Obie są błędne.

6

Obie są błędne, ponieważ kopiujesz do pustego wektora, a kopiowanie wymaga przestrzeni do wstawienia. Nie zmienia rozmiaru samego pojemnika. Co prawdopodobnie trzeba tutaj jest back_insert_iterator i back_inserter:

copy(p, p+5, back_inserter(v)); 
0

Byłoby źle, nawiasem mówiąc, nawet skopiować 1 element do wektora w ten sposób (lub zarezerwować 5 skopiuj ten sposób).

Powodem, dla którego najprawdopodobniej nie jest to uszkodzenie, jest fakt, że osoba wdrażająca uznała, że ​​byłoby nieefektywne przydzielenie pamięci tylko dla jednego elementu, na wypadek gdybyś chciał go później wyhodować, więc może przydzielili wystarczająco dużo dla 16 lub 32 elementów.

Wykonanie najpierw rezerwy (5), a następnie zapisanie bezpośrednio w 5 elementach prawdopodobnie nie byłoby niezdefiniowanym zachowaniem, ale byłoby niepoprawne, ponieważ wektor nie będzie miał logicznego rozmiaru 5, a kopia będzie prawie "zmarnowana", ponieważ wektor będzie twierdził nadal mieć rozmiar 0.

Co to jest poprawne zachowanie to rezerwa (5), wstaw element, przechowuj gdzieś jego iterator, wstaw 4 dodatkowe elementy i spójrz na zawartość pierwszego iteratora. reserve() gwarantuje, że iteratory nie zostaną unieważnione, dopóki wektor nie przekroczy tego rozmiaru, lub wywołanie takie jak: kasowanie(), czyszczenie(), zmiana rozmiaru() lub inna rezerwa().

2

Ale dlaczego nie powoduje to awarii segregatora ?

Ponieważ gwiazdy są wyrównane. Lub uruchomiłeś debugowanie i kompilator zrobił coś, aby "pomóc" tobie. Podsumowując, robisz coś niewłaściwego i wkraczasz w ciemny i niedeterministyczny świat Niezdefiniowanych Zachowań.You reserve jedno miejsce w wektorze, a następnie spróbuj wepchnąć 5 elementów do przestrzeni reserve. Zły.

Masz 3 opcje. W moim osobistym celu preferencji:

1) Użyj back_insert_iterator, który jest przeznaczony właśnie do tego celu. Dostarcza go #include <iterator>. Składnia jest nieco ostry, ale jednak ładny powlekane cukrem skrót back_inserter jest również:

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

2) assign elementy do wektora. Wolę tę metodę nieco mniej po prostu dlatego, że assign jest członkiem vector, i to uderza mnie jako nieco mniej ogólne niż przy użyciu czegoś z algorithm.

3) reserve prawą liczbę elementów, a następnie skopiuj je. Uważam to za ostatni wysiłek na wypadek, gdyby wszystko inne zawiodło z jakiegokolwiek powodu. Jest on oparty na fakcie, że pamięć masowa vector jest ciągła, więc nie jest generyczna i wydaje się, że jest to metoda typu "back-door" polegająca na wprowadzeniu danych do urządzenia vector.

Powiązane problemy