Nachdem wir std :: array haben, welche Verwendungsmöglichkeiten haben Arrays im C-Stil noch?

88

std::arrayist den C-Arrays weit überlegen. Und selbst wenn ich mit Legacy-Code zusammenarbeiten möchte, kann ich ihn einfach verwenden std::array::data(). Gibt es einen Grund, warum ich jemals ein Array der alten Schule haben möchte?

R. Martinho Fernandes
quelle

Antworten:

60

Sofern ich nichts verpasst habe (ich habe die letzten Änderungen im Standard nicht zu genau verfolgt), bleiben die meisten Verwendungen von Arrays im C-Stil bestehen. std::arrayErlaubt zwar eine statische Initialisierung, zählt jedoch die Initialisierer für Sie nicht. Und da die einzige wirkliche Verwendung von Arrays im C-Stil zuvor std::arrayfür statisch initialisierte Tabellen war, wie folgt:

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

Verwenden der üblichen beginund endVorlagenfunktionen (in C ++ 11 übernommen), um diese zu durchlaufen. Ohne jemals die Größe zu erwähnen, die der Compiler aus der Anzahl der Initialisierer ermittelt.

EDIT: Eine andere Sache, die ich vergessen habe: String-Literale sind immer noch Arrays im C-Stil; dh mit Typ char[]. Ich glaube nicht, dass irgendjemand die Verwendung von String-Literalen ausschließen würde, nur weil wir dies getan haben std::array.

James Kanze
quelle
7
Sie können eine variable Funktionsvorlage schreiben, die Arrays erstellt, ohne dass Sie die Länge angeben müssen.
Rechtsfalte
2
Mit C ++ 17 Class Template Deduction wird der automatische Abzug der Anzahl der Initialisierer unterstützt. Beispiel: "auto a = std :: array {1, 2, 3};"
Ricky65
Nitpick: Die Art der String-Literale istconst char[]
Bulletmagnet
30

Nein, um es klar auszudrücken. Und in 30 Zeichen.

Natürlich benötigen Sie C-Arrays, um sie zu implementieren std::array, aber das ist nicht wirklich ein Grund, warum ein Benutzer jemals C-Arrays haben möchte. Darüber hinaus ist no std::arraynicht weniger leistungsfähig als ein C-Array und bietet eine Option für einen grenzüberprüften Zugriff. Und schließlich ist es völlig vernünftig, dass ein C ++ - Programm von der Standardbibliothek abhängt - das ist der Punkt, an dem es Standard ist - und wenn Sie keinen Zugriff auf eine Standardbibliothek haben, ist Ihr Compiler nicht konform und die Die Frage ist mit "C ++" gekennzeichnet, nicht mit "C ++" und den Nicht-C ++ - Dingen, bei denen die Hälfte der Spezifikation fehlt, weil sie dies für unangemessen hielten. "

Hündchen
quelle
1
Hm. Was ist, wenn Sie C ++ - Code schreiben, der aus einer anderen Sprache aufgerufen wird und etwas als Parameter übergeben werden muss?
Asveikau
3
Freistehende Implementierungen dürfen fast die gesamte Standardbibliothek weglassen und sind dennoch konform. Ich hätte ernsthafte Zweifel an einem Compiler, der std::arrayin einer freistehenden C ++ 11-Implementierung nicht implementiert werden konnte .
Dennis Zickefoose
11
C ++ 0x Final Draft (Dokument N3092) § 1.4.7 "Es werden zwei Arten von Implementierungen definiert: gehostet und freistehend. Für eine gehostete Implementierung definiert dieser Internationale Standard den Satz verfügbarer Bibliotheken. Eine freistehende Implementierung ist eine, in der die Ausführung erfolgen kann Sie finden ohne den Vorteil eines Betriebssystems statt und
verfügen
24

Die Verwendung mehrdimensionaler Arrays scheint mit C-Arrays einfacher zu sein als std::array. Zum Beispiel,

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

im Gegensatz zu

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

Aufgrund der automatischen Zerfallseigenschaft von C-Arrays wird c_arr[i]im obigen Beispiel ein Zeiger zerfallen, und Sie müssen nur die verbleibenden Dimensionen als zwei weitere Parameter übergeben. Mein Punkt ist, dass c_arrdas Kopieren nicht teuer ist. Das cpp_arr[i]Kopieren ist jedoch sehr kostspielig.

Sumant
quelle
1
Sie können jedoch eine mehrdimensionale arrayFunktion an eine Funktion übergeben, ohne Dimensionen zu verlieren. Wenn Sie es an eine Funktionsvorlage übergeben, kann diese Funktion sowohl die Dimension als auch die Größe jeder Dimension oder nur eine von beiden ableiten. Dies könnte für wissenschaftliche Vorlagenbibliotheken interessant sein, die hauptsächlich mit beliebigen Dimensionen arbeiten.
Sebastian Mach
29
Ein einfacher template <typename T, int M, int N> using array2d = std::array<std::array<T, N>, M>;sollte eines dieser Probleme lösen.
Miles Rout
6
Das Kopieren Ihres Beispiels c_arrist sehr teuer! Sie müssen den Code dafür selbst angeben. Der Zeiger, auf den er verfällt, entspricht eher einer Referenz als einer Kopie, und Sie können std::arrayeine Referenz übergeben, wenn Sie dies wünschen.
ChetS
1
@MilesRout technisch sollte das nicht std::size_tstatt sein int? Entschuldigung für das Nitpicking, aber das würde es universell machen.
Robbie
1
@ robbie0630 Ja, Sie können es schaffen, size_twenn Sie wollen, obwohl ich mir nicht vorstellen kann, dass es viele Szenarien gibt, in denen Arrays mit mehr als 4 Milliarden Zeilen oder Spalten erforderlich sind.
Miles Rout
13

Wie Sumant sagte, sind mehrdimensionale Arrays mit eingebauten C-Arrays viel einfacher zu verwenden als mit std::array.

Wenn verschachtelt, std::arraykann es sehr schwer zu lesen und unnötig ausführlich werden.

Beispielsweise:

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

verglichen mit

char c_arr[3][3]; 

Beachten Sie auch, dass begin(), end()und size()alle Rückgabe sinnlose Werte , wenn Sie nisten std::array.

Aus diesen Gründen habe ich meine eigene feste Größe mehrdimensionales Array - Container erstellt, array_2dund array_3d. Sie sind analog zu std::arrayaber für mehrdimensionale Arrays mit 2 und 3 Dimensionen. Sie sind sicherer und haben keine schlechtere Leistung als integrierte mehrdimensionale Arrays. Ich habe keinen Container für mehrdimensionale Arrays mit Dimensionen größer als 3 eingefügt, da diese ungewöhnlich sind. In C ++ 0x könnte eine variable Vorlagenversion erstellt werden, die eine beliebige Anzahl von Dimensionen unterstützt.

Ein Beispiel für die zweidimensionale Variante:

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

Die vollständige Dokumentation finden Sie hier:

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

Sie können die Bibliothek hier herunterladen:

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

Ricky65
quelle
4
C-artige Arrays mit fester Größe sind einfach, aber wenn Sie die Abmessungen variieren möchten, werden die Dinge kompliziert. Beispielsweise können arr[x][y]Sie nicht sagen, ob arres sich um ein Array von Arrays, ein Array von Zeigern, einen Zeiger auf ein Array oder einen Zeiger auf einen Zeiger handelt. Alle Implementierungen sind abhängig von Ihren Anforderungen legitim. Und wahrscheinlich erfordern die meisten realen Anwendungsfälle für mehrdimensionale Arrays, dass die Größe zur Laufzeit bestimmt wird.
Keith Thompson
Ich würde gerne diese Variadic-Template-Implementierung von n-dimensionalen Arrays sehen! Meta-Programmierung vom Feinsten!
steffen
3
@steffen Ich habe vor ein paar Jahren einen Versuch gemacht. Sie können es hier anzeigen : code.google.com/p/fsma/source/browse/trunk/… . Testfall hier: code.google.com/p/fsma/source/browse/trunk/… . Ich bin mir sicher, dass es viel besser geht.
Ricky65
5

Die in C ++ verfügbaren Arrays im C-Stil sind tatsächlich viel weniger vielseitig als die echten C-Arrays. Der Unterschied besteht darin, dass Array-Typen in C Laufzeitgrößen haben können. Das Folgende ist gültiger C-Code, kann jedoch weder mit C ++ - Arrays im C-Stil noch mit den C ++ - array<>Typen ausgedrückt werden:

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

In C ++ müssten Sie das temporäre Array auf dem Heap zuweisen:

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

Dies kann nicht erreicht werden std::array<>, da barzur Kompilierungszeit nicht bekannt ist, dass entweder C-artige Arrays in C ++ oder von verwendet werden müssen std::vector<>.


Während das erste Beispiel relativ leicht in C ++ ausgedrückt werden kann (obwohl dies erforderlich ist new[]und delete[]), kann Folgendes in C ++ nicht erreicht werden ohne 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);
}

Der Punkt ist, dass die Zeiger auf die Zeilenarrays int (*)[width]in C ++ keine Laufzeitbreite verwenden können, was jeden Bildmanipulationscode in C ++ viel komplizierter macht als in C. Eine typische C ++ - Implementierung des Bildmanipulationsbeispiels würde folgendermaßen aussehen:

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

Dieser Code führt genau die gleichen Berechnungen wie der obige C-Code durch, muss jedoch die Indexberechnung von Hand durchführen, wo immer die Indizes verwendet werden . Für den 2D-Fall ist dies immer noch möglich (obwohl es viele Möglichkeiten gibt, die Indexberechnung falsch zu machen). Im 3D-Fall wird es allerdings sehr unangenehm.

Ich schreibe gerne Code in C ++. Aber wenn ich mehrdimensionale Daten bearbeiten muss, frage ich mich wirklich, ob ich diesen Teil des Codes nach C verschieben soll.

cmaster - Monica wieder einsetzen
quelle
7
Es ist zu beachten, dass mindestens Clang und GCC VLAs in C ++ unterstützen.
Janus Troelsen
@ JanusTroelsen und auch, dass sie schrecklich begrenzt sind, in welchen Elementtypen sie unterstützen.
Rechtsfalte
Macht C11 VLA nicht optional? Wenn ja, dann denke ich, dass Ihre Antwort irreführend ist. Es wäre richtig, wenn C99 der Standard wäre, aber nicht C11.
Z Boson
1
@Zboson C99 ist ein C-Standard, und es gibt Compiler, die ( gcczum Beispiel) seine VLA-Funktionen implementieren . C11 hat eine Menge interessanter Dinge optional gemacht, und ich glaube nicht, dass das daran liegt, dass sie die Funktion verbieten wollen. Ich neige dazu, es als Zeichen dafür zu sehen, dass sie die Stufe für das Schreiben eines vollständig standardkonformen Compilers senken wollten: VLAs sind ziemlich schwierig zu implementieren, und viel Code kann darauf verzichten, daher ist es für einen neuen Compiler auf einem neuen sinnvoll Plattform, um VLAs nicht sofort implementieren zu müssen.
cmaster
-1

Vielleicht ist das std::arraynicht langsam. Aber ich habe ein Benchmarking mit einfachem Speichern durchgeführt und aus dem std :: array gelesen. Siehe die folgenden Benchmark-Ergebnisse (auf 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

Laut den negativen Markierungen befindet sich der von mir verwendete Code im Pastebin ( Link )

Der Benchmark-Klassencode ist hier ;

Ich weiß nicht viel über Benchmarking ... Mein Code ist möglicherweise fehlerhaft

K'Prime
quelle
6
Benchmark-Ergebnisse ohne Benchmark-Code oder Kompilierungsflags? Komm schon, du kannst es besser machen.
R. Martinho Fernandes
FWIW, nur dieses kleine Stück Code zeigt bereits, dass der Benchmark stark fehlerhaft ist. Ein ausreichend kluger Compiler verwandelt das Ganze einfach inlong test_arr_without_init() { return ARR_SIZE; }
R. Martinho Fernandes
Das war nur ein Beispiel. Ich dachte, es sei keine große Sache. Ich habe den Code geändert, um void / used release build in VS 2013 mit / O2 / Ot / Gl zurückzugeben.
K'Prime
Durch Entfernen des Rückgabewerts kann der Compiler das Ganze void test_arr_without_init() {}jetzt in etwas verwandeln . Sie müssen wirklich durch die Reifen springen, um sicherzustellen, dass der Code, den Sie messen, der Code ist, den Sie messen möchten.
R. Martinho Fernandes
-6
  1. so etwas umsetzen std::array
  2. wenn Sie die STL nicht verwenden möchten oder nicht können
  3. Für die Leistung
Lou Franco
quelle
27
Sagen Sie mir, wie std::arrayweniger performant als ein C-Array sein wird.
Xeo
2
Aus Wikipedia : "Die Array-Implementierung muss keine gebundene Prüfung durchführen. Die Implementierung in Boost erledigt dies jedoch für Operator [], nicht jedoch für Iteratoren." - Operator [] ist also langsamer. Ich habe mir Implementierungen nicht angesehen, aber jeder Code in der Implementierung könnte dem Optimierer im Weg stehen.
Lou Franco
19
@ Aaron McDaid: Das ist nur in at(), es ist nicht in operator[], genau wie std::vector. Es gibt keine Leistungseinbußen oder Aufblähungen des Codes std::array. Der Compiler wurde entwickelt, um solche Dinge zu optimieren. Und natürlich ist das Hinzufügen der aktivierten Funktion ein hervorragendes Debug-Tool und ein großer Vorteil. @ Lou Franco: Der gesamte C ++ - Code hängt möglicherweise von der Standardbibliothek ab - dafür ist er genau das Richtige. @Earlz: Wenn Sie keine STL zur Verfügung haben, dann ist es nicht C ++, und das ist das Ende davon.
Welpe
6
@Earlz: Der C ++ Standard enthält die Standardbibliothek. Wenn Sie die Bibliothek nicht verwenden können, ist sie nicht konform. Und zweitens müssen Sie einen verdammt beschissenen Compiler haben, damit die Verwendung std::arraygrößer ist als die entsprechende C-Array-Verwendung.
Welpe
5
@Earlz: Es gibt einen großen Unterschied zwischen "nicht ganz konform" und "fehlenden Funktionen, die Hunderte von Seiten in der Spezifikation enthalten".
Welpe