Sind std :: vector-Elemente garantiert zusammenhängend?


111

Meine Frage ist einfach: Sind std :: vector-Elemente garantiert zusammenhängend? Kann ich den Zeiger auf das erste Element eines std :: -Vektors als C-Array verwenden?

Wenn mein Gedächtnis mir gut tut, hat der C ++ - Standard keine solche Garantie gegeben. Die Anforderungen an std :: vector waren jedoch so, dass es praktisch unmöglich war, sie zu erfüllen, wenn die Elemente nicht zusammenhängend waren.

Kann jemand das klären?

Beispiel:

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'
    }
}

Ich weiß, dass Sie in Schwierigkeiten sind, wenn Sie valuesin diesem ifBlock mutieren . Ich kenne die Antwort auf Ihre Frage jedoch nicht und hinterlasse nur einen Kommentar. :)
Greg D

@ Greg: Was für ein Problem - kannst du ein wenig näher darauf eingehen?
Reunanen

Ich nehme an, er meinte, dass das Verschieben neuer Werte ein "Realloc" auslösen könnte, das dazu führen würde, dass das Array ungültig wird.
Martin Cote

Mutierte Aufrufe, insbesondere solche values, die ihre Größe ändern (z. B. push_back()), können zu einer Neuzuweisung des zugrunde liegenden Vektors führen, wodurch der kopierte Zeiger ungültig wird array. Es ist das gleiche Prinzip wie bei der Verwendung eines vector :: iterator anstelle eines Zeigers in den Vektor. :)
Greg D

1
Ja, ich habe die `` 's um Werte gesetzt, um zu verdeutlichen, dass ich über die Klasse selbst gesprochen habe, nicht über die darin enthaltenen Werte. :) Unglückliche Benennung und so weiter. Ich denke nicht, dass es wirklich ein Problem im allgemeinen Fall ist, in dem diese Frage relevant ist - warum sollte jemand einen Zeiger auf den Speicher greifen und dann anfangen, mit dem Vektor herumzuspielen, anstatt den Zeiger zu verwenden? Albernheit.
Greg D

Antworten:


118

Dies wurde im eigentlichen C ++ 98-Standard übersehen, aber später als Teil eines TR hinzugefügt. Der bevorstehende C ++ 0x-Standard wird dies natürlich als Voraussetzung enthalten.

Ab n2798 (Entwurf von C ++ 0x):

23.2.6 Klassenvorlagenvektor [Vektor]

1 Ein Vektor ist ein Sequenzcontainer, der Iteratoren mit wahlfreiem Zugriff unterstützt. Darüber hinaus werden (amortisierte) Einfüge- und Löschvorgänge mit konstanter Zeit am Ende unterstützt. Einfügen und Löschen in der Mitte dauert linear. Die Speicherverwaltung erfolgt automatisch, es können jedoch Hinweise zur Verbesserung der Effizienz gegeben werden. Die Elemente eines Vektors werden zusammenhängend gespeichert, was bedeutet, dass wenn v ein Vektor ist, bei dem T ein anderer Typ als bool ist, die Identität & v [n] == & v [0] + n für alle 0 <= n <v gehorcht .Größe().


3
Dies wird auch in ISO 14882, 2. Ausgabe: Abschnitt 23.2.4 [lib.vector] angegeben: "Die Elemente eines Vektors werden zusammenhängend gespeichert, was bedeutet, dass wenn v ein Vektor <T, Allocator> ist, wobei T ein anderer Typ als ist bool, dann gehorcht es der Identität & v [n] == & v [0] + n für alle 0 <= n <v.size (). "
Mike Caron

4
so s, TR, TC, :) Eigentlich heißt C ++ 03 auch C ++ 98-TC1 (technische Berichtigung) von dem, was ich gelesen habe
Johannes Schaub - litb

2
Was ist mit Vektoren von Vektoren? İnner-Vektoren liegen direkt hinter den inneren Vektoren der letzten Gruppe?
Huseyin Tugrul Buyukisik

1
@huseyin tugrul buyukisik hast du die antwort darauf gelernt? Ich frage mich auch, wie das funktioniert
David Doria

1
@huseyin tugrul buyukisik Es ist natürlich wahr, aber es sind die Instanzen der nachfolgenden std::vector, die zusammenhängend sind. ZB .: in std::vector<std::vector<int>> vden Elementen v[0], v[1]... wird anschließend in einem Speicher gespeichert, aber das Element v[0].back()und v[1].front()nicht als garantiert.
Jarzec

27

Wie andere Antworten gezeigt haben, ist der Inhalt eines Vektors garantiert kontinuierlich (mit Ausnahme der Verrücktheit von Bool).

Der Kommentar, den ich hinzufügen wollte, lautet: Wenn Sie den Vektor einfügen oder löschen, wodurch der Vektor seinen Speicher neu zuordnen kann, werden alle Ihre gespeicherten Zeiger und Iteratoren ungültig.


1
Die Elemente würden immer noch in einem zusammenhängenden Speicherblock gespeichert, sondern nur an einem anderen Ort. Die Frage betraf speziell die Kontiguität.
Dima

2
Die vorhandenen Zeiger und Iteratoren würden jedoch ungültig.
Bill Lynch

Guter Punkt. Sie sollten dies in Ihre Antwort aufnehmen, um zu klären, was Sie meinen.
Dima

nützlichste Antwort auf mich
CoffeDeveloper

Jetzt weiß ich, warum mein Programm gestern fehlerhaft war, als ich es in einer Doppelschleife durchlief und bestimmte Elemente entfernte :) Danke!
user2891462

9

Der Standard garantiert tatsächlich, dass a vectorim Speicher kontinuierlich ist und &a[0]an eine CFunktion übergeben werden kann, die ein Array erwartet.

Die Ausnahme von dieser Regel ist, vector<bool>dass nur ein Bit pro verwendet wird, boolobwohl es einen kontinuierlichen Speicher hat, kann es nicht als verwendet werden bool*(dies wird allgemein als falsche Optimierung und als Fehler angesehen).

Übrigens, warum benutzt du keine Iteratoren? Dafür sind sie da.


1
> Übrigens, warum verwenden Sie keine Iteratoren? Dafür sind sie da. Vielleicht hat er das neue Papier von Alexanrescu
Nemanja Trifunovic

Vielen Dank für den Link, ich werde es zu meiner Leseliste (ich versuche, Alexandresus Artikel nicht zu verpassen)
Motti

Mwahaha, heutzutage scheinen alle über diese Präsentation zu sprechen. Schauen Sie, die Diskussion darüber ist immer noch heiß: groups.google.com/group/comp.lang.c++.moderated/browse_thread/…
Johannes Schaub - litb

Wenn Sie es sorgfältig lesen, heißt es in Alexandrescus Artikel nicht wirklich "Verwenden Sie keine Iteratoren in C ++", sondern "Check out D". Der Ansatz, den er in diesem Artikel beschreibt, ähnelt auffallend allen vorhandenen Sprachen und Frameworks, die das funktionale Erbe übernommen haben (List, Scheme, Haskell), und ich bezweifle ernsthaft, ob eine weitere C-basierte Syntax ein idealer Ausgangspunkt für eine bessere ist Listenbehandlung. Irgendwann im letzten Jahr habe ich kurz versucht, ihn davon zu überzeugen, seine beträchtlichen Talente für die Verbesserung einer bereits etablierten Sprache wie C # einzusetzen, aber ich fürchte keinen Erfolg! :)
Daniel Earwicker

6

Wie andere bereits gesagt haben, wird vectorintern eine zusammenhängende Anordnung von Objekten verwendet. Zeiger in dieses Array sollten immer dann als ungültig behandelt werden, wenn eine nicht konstante Mitgliedsfunktion als IIRC bezeichnet wird.

Es gibt jedoch eine Ausnahme !!

vector<bool>hat eine spezielle Implementierung, die Platz spart, so dass jeder Bool nur ein Bit verwendet. Das zugrunde liegende Array ist kein zusammenhängendes Array von Bool, und die Array-Arithmetik vector<bool>funktioniert nicht wie vector<T>gewünscht.

(Ich nehme an, es ist auch möglich, dass dies für jede Spezialisierung von Vektoren gilt, da wir immer eine neue implementieren können. Dies std::vector<bool>ist jedoch die einzige, ähm, Standardspezialisierung, bei der einfache Zeigerarithmetik nicht funktioniert.)


Der Benutzer darf sich nicht spezialisieren std::vector, und alle anderen Vektoren müssen zusammenhängenden Speicher verwenden. Daher std::vector<bool>ist (zum Glück) der einzige Standardvektor, der seltsam ist. (Ich bin der festen Überzeugung, dass diese Spezialisierung veraltet und durch z. B. eine std::dynamic_bitsetmit nahezu der gleichen Funktionalität ersetzt werden sollte. Es ist keine schlechte Datenstruktur, es ist nur kein Vektor.)
Arne Vogel

3

Ich habe diesen Thread gefunden, weil ich einen Anwendungsfall habe, bei dem Vektoren, die zusammenhängenden Speicher verwenden, von Vorteil sind.

Ich lerne, wie man Vertex-Pufferobjekte in OpenGL verwendet. Ich habe eine Wrapper-Klasse erstellt, die die Pufferlogik enthält. Ich muss also nur ein Array von Floats und einige Konfigurationswerte übergeben, um den Puffer zu erstellen. Ich möchte in der Lage sein, einen Puffer aus einer Funktion basierend auf Benutzereingaben zu generieren, daher ist die Länge zur Kompilierungszeit nicht bekannt. So etwas zu tun wäre die einfachste Lösung:

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

Jetzt kann ich die Floats des Vektors als Array an die pufferbezogenen Funktionen von OpenGL übergeben. Dadurch entfällt auch die Notwendigkeit von sizeof, um die Länge des Arrays zu bestimmen.

Dies ist weitaus besser, als ein großes Array zum Speichern der Floats zuzuweisen und zu hoffen, dass ich es groß genug gemacht habe, oder mein eigenes dynamisches Array mit zusammenhängendem Speicher zu erstellen.


2
Diese Funktion macht für mich keinen Sinn. Wollen Sie eine Referenz oder einen Zeiger auf sich selbst vund nicht auf sich vselbst übergeben? Wenn Sie valleine übergeben, wird eine Kopie innerhalb der Funktion erstellt, die nach Beendigung der Funktion nicht mehr vorhanden ist. Sie drücken also etwas auf den Vektor, um den Vektor nur zu löschen, wenn die Funktion endet.
Johnbakers

1

cplusplus.com:

Vektorcontainer werden als dynamische Arrays implementiert. Wie bei regulären Arrays werden auch bei Vektorcontainern die Elemente an zusammenhängenden Speicherorten gespeichert. Dies bedeutet, dass auf ihre Elemente nicht nur mithilfe von Iteratoren, sondern auch mithilfe von Offsets für reguläre Zeiger auf Elemente zugegriffen werden kann.


1

Ja, die Elemente eines std :: vector sind garantiert zusammenhängend.


Richtig. Ich denke, ich benutze zu viel davon :)
Benoît
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.