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.