Ora che abbiamo std :: array quali usi sono rimasti per gli array in stile C?


89

std::arrayè di gran lunga superiore agli array C. E anche se voglio interagire con il codice legacy, posso semplicemente usare std::array::data(). C'è qualche motivo per cui vorrei mai un array della vecchia scuola?

Risposte:


60

A meno che non mi sia perso qualcosa (non ho seguito troppo da vicino le modifiche più recenti allo standard), la maggior parte degli usi degli array in stile C. std::arrayconsente l'inizializzazione statica, ma non conterà ancora gli inizializzatori per te. E poiché l'unico vero uso di array in stile C prima std::arrayera per tabelle inizializzate staticamente sulla falsariga di:

MyStruct const table[] =
{
    { something1, otherthing1 },
    //  ...
};

usando le solite funzioni begine endtemplate (adottate in C ++ 11) per iterarle. Senza mai menzionare la dimensione, che il compilatore determina dal numero di inizializzatori.

EDIT: Un'altra cosa che ho dimenticato: i letterali stringa sono ancora array in stile C; cioè con il tipo char[]. Non penso che qualcuno escluderebbe l'uso di stringhe letterali solo perché abbiamo std::array.


7
È possibile scrivere un modello di funzione variadica che costruisce array senza che sia necessario specificare la lunghezza.
destra

2
Con C ++ 17 Class Template Deduction è supportata la deduzione automatica del numero di inizializzatori. Ad esempio, "auto a = std :: array {1, 2, 3};"
Ricky65

Nitpick: il tipo di stringhe letterali èconst char[]
Bulletmagnet

31

No. Per, uh, per dirla senza mezzi termini. E in 30 caratteri.

Ovviamente, sono necessari array C per l'implementazione std::array, ma non è davvero un motivo per cui un utente vorrebbe mai array C. Inoltre, no, std::arraynon è meno performante di un array C e ha un'opzione per un accesso controllato dai limiti. E infine, è del tutto ragionevole che qualsiasi programma C ++ dipenda dalla libreria Standard - questo è il punto che è Standard - e se non hai accesso a una libreria Standard, il tuo compilatore non è conforme e il la domanda è etichettata "C ++", non "C ++ e quelle cose non-C ++ che perdono metà della specifica perché lo ritengono inappropriato.".


1
Hm. Cosa succede se stai scrivendo codice C ++ che viene chiamato da un altro linguaggio e necessita di qualcosa da passare come parametro?
asveikau

3
Le implementazioni indipendenti possono tralasciare quasi tutta la libreria standard ed essere comunque conformi. std::arrayTuttavia, avrei seri dubbi su un compilatore che non potrebbe essere implementato in un'implementazione C ++ 11 indipendente.
Dennis Zickefoose

11
C ++ 0x Final Draft (Documento N3092) § 1.4.7 "Sono definiti due tipi di implementazioni: ospitata e indipendente. Per un'implementazione ospitata, questo standard internazionale definisce l'insieme di librerie disponibili. Un'implementazione indipendente è quella in cui l'esecuzione può si svolgono senza il vantaggio di un sistema operativo e ha un set di librerie definito dall'implementazione che include alcune librerie di supporto del linguaggio ".. L'STL non è incluso come libreria di" supporto del linguaggio "in un compilatore indipendente
Earlz

24

Sembra usando array multidimensionali è più facile con matrici C rispetto std::array. Per esempio,

char c_arr[5][6][7];

al contrario di

std::array<std::array<std::array<char, 7>, 6>, 5> cpp_arr;

Anche a causa della proprietà di decadimento automatico degli array C, c_arr[i]nell'esempio precedente decadrà in un puntatore e devi solo passare le dimensioni rimanenti come altri due parametri. Il punto è che c_arrnon è costoso da copiare. Tuttavia, cpp_arr[i]sarà molto costoso da copiare.


1
Tuttavia, potresti passare un multidimensionale arraya una funzione senza perdere le dimensioni. E se lo passi a un modello di funzione, quella funzione potrebbe dedurre sia la dimensione che la dimensione di ciascuna dimensione, o solo una delle due. Questo potrebbe essere interessante per le librerie di modelli scientifici che lavorano principalmente su dimensioni arbitrarie.
Sebastian Mach

31
un semplice template <typename T, int M, int N> using array2d = std::array<std::array<T, N>, M>;dovrebbe risolvere uno qualsiasi di questi problemi.
Rotta in miglia

7
Il tuo esempio c_arrè molto costoso da copiare! Devi fornire il codice per farlo da solo. Il puntatore a cui decade è un equivalente più vicino a un riferimento che a una copia e puoi usarlo std::arrayper passare un riferimento se è quello che vuoi.
ChetS

1
@MilesRout tecnicamente , non dovrebbe essere al std::size_tposto di int? scusate il pelo nell'uovo, ma questo lo renderebbe universale.
robbie

1
@ robbie0630 Sì, puoi farlo size_tse vuoi, anche se non riesco a immaginare che ci siano molti scenari in cui sono necessari array con più di 4 miliardi di righe o colonne.
Miglia in rotta

14

Come ha detto Sumant, gli array multidimensionali sono molto più facili da usare con gli array C incorporati che con std::array.

Quando annidato, std::arraypuò diventare molto difficile da leggere e inutilmente prolisso.

Per esempio:

std::array<std::array<int, 3>, 3> arr1; 

rispetto a

char c_arr[3][3]; 

Inoltre, tieni presente che begin(), end()e size()tutti restituiscono valori privi di significato quando annidi std::array.

Per questi motivi ho creato i miei contenitori di array multidimensionali a dimensione fissa array_2de array_3d. Sono analoghi std::arrayma per array multidimensionali di 2 e 3 dimensioni. Sono più sicuri e non hanno prestazioni peggiori degli array multidimensionali incorporati. Non ho incluso un contenitore per array multidimensionali con dimensioni maggiori di 3 poiché sono rari. In C ++ 0x è possibile creare una versione del modello variadico che supporti un numero arbitrario di dimensioni.

Un esempio della variante bidimensionale:

//Create an array 3 x 5 (Notice the extra pair of braces) 

fsma::array_2d <double, 3, 5> my2darr = {{
    { 32.19, 47.29, 31.99, 19.11, 11.19},
    { 11.29, 22.49, 33.47, 17.29, 5.01 },
    { 41.97, 22.09, 9.76, 22.55, 6.22 }
}};

La documentazione completa è disponibile qui:

http://fsma.googlecode.com/files/fsma.html

Puoi scaricare la libreria qui:

http://fsma.googlecode.com/files/fsma.zip


4
Gli array in stile C a dimensione fissa sono facili, ma se vuoi variare le dimensioni le cose si complicano. Ad esempio, dato arr[x][y], non puoi dire se arrè un array di array, un array di puntatori, un puntatore a un array o un puntatore a un puntatore; tutte le implementazioni sono legittime, a seconda delle esigenze. E probabilmente la maggior parte dei casi d'uso del mondo reale per array multidimensionali richiede che la dimensione sia determinata in fase di esecuzione.
Keith Thompson

Mi piacerebbe vedere quell'implementazione del modello variadico di array n-dimensionali! Meta-programmazione al suo meglio!
steffen

3
@steffen ho fatto un tentativo alcuni anni fa. Puoi visualizzarlo qui: code.google.com/p/fsma/source/browse/trunk/… . Scenario di test qui: code.google.com/p/fsma/source/browse/trunk/… . Sono sicuro che si può fare molto meglio però.
Ricky65

5

Gli array in stile C disponibili in C ++ sono in realtà molto meno versatili degli array C reali. La differenza è che in C, i tipi di array possono avere dimensioni di runtime . Quanto segue è codice C valido, ma non può essere espresso né con array in stile C C ++ né con i array<>tipi C ++ :

void foo(int bar) {
    double tempArray[bar];
    //Do something with the bar elements in tempArray.
}

In C ++, dovresti allocare l'array temporaneo sull'heap:

void foo(int bar) {
    double* tempArray = new double[bar];
    //Do something with the bar elements behind tempArray.
    delete[] tempArray;
}

Questo non può essere ottenuto con std::array<>, perché barnon è noto in fase di compilazione, richiede l'uso di array in stile C in C ++ o di std::vector<>.


Mentre il primo esempio potrebbe essere espresso in modo relativamente facile in C ++ (sebbene richieda new[]e delete[]), non è possibile ottenere quanto segue in C ++ senza std::vector<>:

void smoothImage(int width, int height, int (*pixels)[width]) {
    int (*copy)[width] = malloc(height*sizeof(*copy));
    memcpy(copy, pixels, height*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y][x] = //compute smoothed value based on data around copy[y][x]
        }
    }
    free(copy);
}

Il punto è che i puntatori agli array di linee int (*)[width]non possono utilizzare una larghezza di runtime in C ++, il che rende qualsiasi codice di manipolazione delle immagini molto più complicato in C ++ rispetto a C. Una tipica implementazione C ++ dell'esempio di manipolazione delle immagini sarebbe simile a questa:

void smoothImage(int width, int height, int* pixels) {
    int* copy = new int[height*width];
    memcpy(copy, pixels, height*width*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y*width + x] = //compute smoothed value based on data around copy[y*width + x]
        }
    }
    delete[] copy;
}

Questo codice esegue esattamente gli stessi calcoli del codice C precedente, ma deve eseguire il calcolo dell'indice a mano ovunque vengano utilizzati gli indici . Per il caso 2D, questo è ancora fattibile (anche se offre molte opportunità per sbagliare il calcolo dell'indice). Tuttavia, diventa davvero brutto nel caso 3D.

Mi piace scrivere codice in C ++. Ma ogni volta che ho bisogno di manipolare dati multidimensionali, mi chiedo davvero se devo spostare quella parte del codice in C.


7
Va notato che almeno Clang e GCC supportano i VLA in C ++.
Janus Troelsen

@JanusTroelsen e anche che sono orribilmente limitati nei tipi di elementi che supportano.
destra

C11 non rende VLA opzionale? Se è così, penso che la tua risposta sia fuorviante. Sarebbe corretto quando C99 era lo standard ma non C11.
Bosone Z

1
@Zboson C99 è uno standard C e ci sono compilatori che implementano le sue funzionalità VLA ( gccad esempio). C11 ha reso opzionali un bel po 'di cose interessanti, e non credo che sia perché vogliono mettere fuori legge la funzione. Tendo a vederlo come un segno che volevano abbassare il livello per scrivere un compilatore conforme agli standard: i VLA sono piuttosto difficili da implementare e molto codice può fare a meno, quindi ha senso per un nuovo compilatore su qualche nuovo piattaforma per non dover implementare immediatamente i VLA.
cmaster - ripristina monica il

-1

Forse std::arraynon è lento. Ma ho fatto un po 'di benchmarking usando simple store e ho letto da std :: array; Vedere i risultati del benchmark di seguito (su W8.1, VS2013 Update 4):

ARR_SIZE: 100 * 1000
Avrg = Tick / ARR_SIZE;

test_arr_without_init
==>VMem: 5.15Mb
==>PMem: 8.94Mb
==>Tick: 3132
==>Avrg: 0.03132
test_arr_with_init_array_at
==>VMem: 5.16Mb
==>PMem: 8.98Mb
==>Tick: 925
==>Avrg: 0.00925
test_arr_with_array_at
==>VMem: 5.16Mb
==>PMem: 8.97Mb
==>Tick: 769
==>Avrg: 0.00769
test_c_arr_without_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 358
==>Avrg: 0.00358
test_c_arr_with_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 305
==>Avrg: 0.00305

Secondo i segni negativi, il codice che ho usato è nel pastebin ( link )

Il codice della classe benchmark è qui ;

Non so molto sui benchmarking ... Il mio codice potrebbe essere difettoso


6
Risultati benchmark senza codice benchmark o flag di compilazione? Dai, puoi fare di meglio.
R. Martinho Fernandes

FWIW, solo quel piccolo pezzo di codice mostra già che il benchmark è gravemente difettoso. Un compilatore abbastanza intelligente trasformerà il tutto inlong test_arr_without_init() { return ARR_SIZE; }
R. Martinho Fernandes

Quello era solo un esempio. Ho pensato che non fosse un grosso problema. Ho cambiato il codice per restituire void, build di rilascio usata in VS 2013, con / O2 / Ot / Gl.
K'Prime

La rimozione del valore restituito significa che il compilatore può trasformare l'intera cosa in void test_arr_without_init() {}ora. Hai davvero bisogno di saltare attraverso i cerchi per assicurarti che il codice che stai misurando sia il codice che vuoi misurare.
R. Martinho Fernandes

-5
  1. per implementare qualcosa di simile std::array
  2. se non vuoi usare l'STL o non puoi
  3. Per le prestazioni

27
Dimmi come std::arraysarà meno performante di un array C.
Xeo

2
Da wikipedia : "L'implementazione dell'array non è richiesta per eseguire il bound check. Tuttavia l'implementazione in boost lo fa per l'operatore [], ma non per gli iteratori." - quindi l'operatore [] è più lento. Non ho esaminato le implementazioni, ma qualsiasi codice nell'implementazione potrebbe intralciare l'ottimizzatore.
Lou Franco

19
@Aaron McDaid: È solo dentro at(), non è dentro operator[], proprio come std::vector. Non c'è diminuzione delle prestazioni o aumento del codice std::array, il compilatore è progettato per ottimizzare questo tipo di cose. E, naturalmente, l'aggiunta della funzione selezionata è un eccellente strumento di debug e un grande vantaggio. @Lou Franco: Tutto il codice C ++ può dipendere dalla libreria Standard, questo è un po 'a cosa serve. @Earlz: Se non hai STL disponibile, allora non è C ++, e questa è la fine.
Puppy

6
@Earlz: lo standard C ++ contiene la libreria standard. Se non puoi usare la libreria, non è conforme. E in secondo luogo, devi avere un compilatore di merda perché l'uso di std::arraysia più grande dell'uso equivalente dell'array C.
Puppy

5
@Earlz: C'è una grande differenza tra "non del tutto conforme" e "caratteristiche mancanti che sono centinaia di pagine nelle specifiche".
Puppy
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.