Gli elementi std :: vector sono garantiti per essere contigui?


111

La mia domanda è semplice: gli elementi std :: vector sono garantiti per essere contigui? In ordine di parola, posso usare il puntatore al primo elemento di uno std :: vector come un C-array?

Se la mia memoria mi serve bene, lo standard C ++ non offre tale garanzia. Tuttavia, i requisiti std :: vector erano tali che era virtualmente impossibile soddisfarli se gli elementi non erano contigui.

Qualcuno può chiarire questo?

Esempio:

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

So che sei nei guai se muti valuesall'interno di quel ifblocco. Non conosco la risposta alla tua domanda, quindi lascio solo un commento. :)
Greg D,

@ Greg: Che guai - puoi approfondire un po '?
Riunione

Suppongo che volesse dire che l'inserimento di nuovi valori può attivare un "rialloc" che renderebbe l'array non valido.
Martin Cote,

Le chiamate che mutano values, in particolare che cambiano la sua dimensione (ad esempio push_back()), possono richiedere una riallocazione del vettore sottostante che invalida il puntatore in cui è stato copiato array. È lo stesso principio alla base dell'uso di un vettore :: iteratore invece di un puntatore nel vettore. :)
Greg D,

1
Sì, ho inserito i `` 's attorno ai valori per cercare di chiarire che stavo parlando della classe stessa, non dei valori contenuti al suo interno. :) Denominazione sfortunata e tutto il resto. Non penso che sia davvero un problema nel caso generale in cui questa domanda è rilevante, però: perché qualcuno dovrebbe prendere un puntatore alla memoria, quindi iniziare a masticare con il vettore invece di usare il puntatore? Stupidità.
Greg D,

Risposte:


118

Questo era mancato dallo standard C ++ 98 vero e proprio, ma successivamente aggiunto come parte di un TR. Il prossimo standard C ++ 0x lo conterrà ovviamente come requisito.

Da n2798 (bozza di C ++ 0x):

23.2.6 Modello di classe vettore [vettore]

1 Un vettore è un contenitore di sequenze che supporta iteratori ad accesso casuale. Inoltre, supporta operazioni di inserimento e cancellazione a tempo costante (ammortizzato) alla fine; inserire e cancellare nel mezzo richiede tempo lineare. La gestione dello storage viene gestita automaticamente, sebbene sia possibile fornire suggerimenti per migliorare l'efficienza. Gli elementi di un vettore sono memorizzati in modo contiguo, il che significa che se v è un vettore dove T è un tipo diverso da bool, allora obbedisce all'identità & v [n] == & v [0] + n per tutti 0 <= n <v .taglia().


3
Questo è anche affermato in ISO 14882, 2a edizione: Sezione 23.2.4 [lib.vector]: "Gli elementi di un vettore sono memorizzati in modo contiguo, il che significa che se v è un vettore <T, Allocatore> dove T è un tipo diverso da bool, quindi obbedisce all'identità & v [n] == & v [0] + n per tutti 0 <= n <v.size (). "
Mike Caron,

4
so s, TR, TC, :) In realtà C ++ 03 è anche chiamato C ++ 98-TC1 (rettifica tecnica) da quello che ho letto
Johannes Schaub - litb

2
E i vettori di vettori? I vettori interni sono subito dopo i vettori interni dell'ultimo gruppo?
huseyin tugrul buyukisik

1
@huseyin tugrul buyukisik hai imparato la risposta a questo? Mi chiedo anche come funzioni
David Doria

1
@huseyin tugrul buyukisik Ovviamente è vero, ma sono le istanze successive std::vectorche sono contigue. Es .: in std::vector<std::vector<int>> velementi v[0], v[1], ... sono memorizzate successivamente nella memoria, ma l'elemento v[0].back()e v[1].front()non sono garantiti per essere.
jarzec

27

Come altre risposte hanno sottolineato, il contenuto di un vettore è garantito per essere continuo (eccetto la stranezza di bool).

Il commento che volevo aggiungere è che se fai un inserimento o una cancellazione sul vettore, che potrebbe causare il vettore a riallocare la sua memoria, allora tutti i tuoi puntatori e iteratori salvati verranno invalidati.


1
Gli elementi sarebbero ancora memorizzati in un blocco di memoria contiguo, sarebbe solo in un posto diverso. La domanda riguardava specificamente la contiguità.
Dima

2
Ma i puntatori e gli iteratori esistenti sarebbero invalidati.
Bill Lynch,

Buon punto. Dovresti inserirlo nella tua risposta per chiarire cosa intendi.
Dima

risposta più utile per me
CoffeDeveloper

Ora so perché ieri il mio programma era in segfault, quando l'ho eseguito in un doppio ciclo rimuovendo alcuni elementi :) Grazie!
user2891462

9

Lo standard infatti garantisce che a vectorsia continuo in memoria e che &a[0]possa essere passato a una Cfunzione che si aspetta un array.

L'eccezione a questa regola è vector<bool>che utilizza solo un bit per boolquindi, sebbene abbia una memoria continua, non può essere utilizzata come un bool*(questo è ampiamente considerato come una falsa ottimizzazione e un errore).

BTW, perché non usi gli iteratori? Ecco a cosa servono.


1
> BTW, perché non usi gli iteratori? Ecco a cosa servono. Forse ha letto il nuovo articolo di Alexanrescu
Nemanja Trifunovic

Grazie per il link, ci penso io alla mia lista di lettura (cerco di non perdere gli articoli di Alexandresu)
Motti

Mwahaha, sembra che tutti stiano parlando di quella presentazione in questi giorni. Guarda, la discussione è ancora calda a riguardo: groups.google.com/group/comp.lang.c++.moderated/browse_thread/…
Johannes Schaub - litb

Se lo leggi attentamente, l'articolo di Alexandrescu in realtà non dice "Non usare iteratori in C ++", dice "Controlla D". L'approccio che descrive in quel documento è sorprendentemente simile a qualsiasi linguaggio e framework esistente che abbia assorbito l'eredità funzionale (List, Scheme, Haskell) e dubito seriamente che l'ennesima sintassi basata sul C sia un punto di partenza ideale per migliorare gestione delle liste. Qualche volta l'anno scorso ho cercato brevemente di persuaderlo a trasformare i suoi notevoli talenti verso il miglioramento di un linguaggio già consolidato come il C #, ma con temo di non avere successo! :)
Daniel Earwicker

6

Come altri hanno già detto, vector utilizza internamente un array contiguo di oggetti. I puntatori in quell'array dovrebbero essere considerati non validi ogni volta che una funzione membro non const viene chiamata IIRC.

Tuttavia, c'è un'eccezione !!

vector<bool>ha un'implementazione specializzata progettata per risparmiare spazio, in modo che ogni bool utilizzi solo un bit. L'array sottostante non è un array contiguo di bool e l'aritmetica dell'array su vector<bool>non funziona come vector<T>farebbe.

(Suppongo che sia anche possibile che questo possa essere vero per qualsiasi specializzazione del vettore, dal momento che possiamo sempre implementarne una nuova. Tuttavia, std::vector<bool>è l'unica, ehm, specializzazione standard su cui la semplice aritmetica dei puntatori non funzionerà.)


L'utente non è autorizzato a specializzarsi std::vectore tutti gli altri vettori devono utilizzare l'archiviazione contigua. Pertanto, std::vector<bool>è (fortunatamente) l'unico vettore standard che è strano. (Sono fermamente dell'opinione che questa specializzazione dovrebbe essere deprecata e sostituita, ad esempio, da una std::dynamic_bitsetcon la stessa funzionalità. Non è una cattiva struttura dati, semplicemente non è un vettore.)
Arne Vogel

3

Ho trovato questo thread perché ho un caso d'uso in cui i vettori che utilizzano la memoria contigua sono un vantaggio.

Sto imparando a usare gli oggetti del buffer dei vertici in OpenGL. Ho creato una classe wrapper per contenere la logica del buffer, quindi tutto ciò che devo fare è passare un array di float e alcuni valori di configurazione per creare il buffer. Voglio essere in grado di generare un buffer da una funzione basata sull'input dell'utente, quindi la lunghezza non è nota in fase di compilazione. Fare qualcosa del genere sarebbe la soluzione più semplice:

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

Ora posso passare i float del vettore come array alle funzioni relative al buffer di OpenGL. Ciò elimina anche la necessità di sizeof per determinare la lunghezza dell'array.

Questo è molto meglio che allocare un array enorme per memorizzare i float e sperare di averlo reso abbastanza grande, o creare il mio array dinamico con memoria contigua.


2
questa funzione non ha alcun senso per me. intendi passare un riferimento o un puntatore a vpiuttosto che a vse stesso? perché il passaggio vda solo provocherà una copia all'interno della funzione, che cesserà di esistere al termine della funzione. Quindi stai spingendo qualcosa sul vettore solo per eliminare il vettore quando la funzione termina.
johnbakers

1

cplusplus.com:

I contenitori vettoriali vengono implementati come array dinamici; Proprio come gli array normali, i contenitori vettoriali hanno i loro elementi memorizzati in posizioni di archiviazione contigue, il che significa che è possibile accedere ai loro elementi non solo utilizzando iteratori ma anche utilizzando offset su puntatori regolari agli elementi.


1

Sì, è garantito che gli elementi di uno std :: vector siano contigui.


Destra. Immagino di usarne troppi :)
Benoît
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.