2016-04-27 9 views
2

Ich habe mich für die D-Sprache interessiert und fing an, sie zu lernen, weil ich der Meinung bin, dass sie einfacheres und effizienteres Codieren als C oder C++ unterstützt, während sie den Code in diesen anderen Sprachen verwendet.Wie erstellt man kontinuierliche mehrdimensionale Arrays in D, die mit C-Code verbunden werden können?

jedoch versucht, eine 2D-Array zu konstruieren ich das beobachtet:

auto a = new double[][](1000,3); // clean and seemingly elegant 
a.length = 0;      // To fill the array by ~= operator 
a ~= [1, 2, 3];      // fill the first row 
writeln(a);       // -> [[1, 2, 3]] (OK) 
writeln(a.capacity,a[0].capacity); // -> 1 3 (should be 1000 3) 

Es schon falsch anfühlt, als ich den Wunsch explizit ausgedrückt Speicher für 1000x3 Zahlen zu reservieren. Sowohl Digital Mars D als auch GCC gdc Compiler erzeugen jedoch das gleiche Ergebnis.

Um das Array wirklich zweidimensional zu machen, habe ich eine weitere Zeile hinzugefügt. einen festen Block des Speichers mit der Größe des 3*3*double.sizeof und zeilenweise besetzten Layout

a ~= [3, 4, 5];      // add another row 
writeln(a);       // -> [[1, 2, 3], [4, 5, 6]] (OK) 
writeln(a.capacity,a[0].capacity); // -> 3 3 

akzeptabel Dies könnte, wenn es, dass der Inhalt a gemeint. Aber mehr Überraschungen auf dem Weg - wenn ich das Layout dieser Anordnung aus der C-Sicht überprüfen will:

double* p = &(a[0][0]);    // get a C-like pointer to the data 
for(size_t i=0; i<20; i++) { 
    writef("%.0f ", *(p+i));  // should be: 1 2 3 4 5 6 nan nan ... 
    } 
writeln(); 

Ich habe verwirrende Ergebnisse und verschiedene unter Compiler. DMD mit libphobos2 (Version 2.071.0) sagte:

1 2 3 0 0 0 0 0 4 5 6 0 0 0 0 0 0 0 0 0 

aber mit GDC (GCC Version 4.8.4) der gleiche Code dies gedruckt:

1 2 3 0 nan nan nan 0 nan nan nan 0 nan nan nan 0 nan nan nan 0 

Ups. Dies ist weder verwendbar noch tragbar. Entweder ist das Array überhaupt nicht kontinuierlich (daher lese ich einen anderen Speicher), oder beide Implementierungen sind fehlerhaft, weil die Initialisierung mit nan garantiert sein sollte. OK, ich weiß schon, dass ich malloc verwenden kann und alles so machen kann, wie ich es in C machen würde, aber dann sehe ich nicht den Vorteil, ein anderes riesiges Biest eine Sprache mit eigenen schrulligen Hacks zu lernen.

Gibt es eine saubere Möglichkeit, kontinuierliche mehrdimensionale Arrays mit C-Layout zu erstellen, ohne die Vorteile von D wie Speicherverwaltung, Bereiche, Slices, Algorithmen usw. zu verlieren?

Edit:

Nach der Antwort von @weltensturm ich meine Fehler behoben und ich habe ein wenig mehr Tests. Ich brauche tatsächlich ein dynamisches Array, weil ich die Zahlen aus FIFO lese und ich weiß nicht a priori, wie viele Zeilen zu erwarten sind. Dieser Code offenbar funktioniert:

auto as = new double[3][0];  // Inconsistent w/C: this is 0 rows by 3 columns 
auto ad = new double[][](0,3); // also 0 x 3 
as ~= [1,2,3]; as ~= [4,5,6]; 
ad ~= [1,2,3]; ad ~= [4,5,6]; // I can append to both 
writeln(as);     // -> [[1, 2, 3], [4, 5, 6]] 
writeln(ad);     // -> [[1, 2, 3], [4, 5, 6]] 
writefln("as capacity: %dx%d", as.capacity, as[0].capacity); 
           // -> as capacity: 2x0 
writefln("ad capacity: %dx%d", ad.capacity, ad[0].capacity); 
           // -> ad capacity: 3x3 
double* p = &(as[0][0]); 
for(size_t i=0; i<6; i++) writef("%.0f ", *(p+i)); // -> 1 2 3 4 5 6 
writeln(); 
p = &(ad[0][0]); 
for(size_t i=0; i<9; i++) writef("%.0f ", *(p+i)); // -> 1 2 3 4 5 6 0 0 0 
writeln();      

Sowohl as und ad sind tatsächlich dynamisch und werden auf Anfügen der Größe verändert, aber sie verhalten sich anders - Blick auf die Kapazitäten nach den anhängt.

Nun ist die Frage, kann ich auf das Verhalten einer dieser Alternativen verlassen? Ist die Sprache mindestens für die new double[3][0] Syntax garantiert? Ist es möglich, das Layout und die Kontinuität für das "dynamische" Array (new double[][](0,3)) zu garantieren, vorausgesetzt, ich weiß, was nicht zu tun (z. B. nicht die Eigenschaft length direkt ändern)?

+0

Sie haben ein dynamisches Array von dynamischen Arrays. Sie benötigen einen kontinuierlichen Speicherbedarf, um die innere Dimension statisch zu halten. –

Antwort

3

Sie verwenden dynamische Arrays.Beachten Sie, dass sie einen einstellbaren Längenwert haben, der in Ihrem GDC-Beispiel als Nullen angezeigt wird, die nan s unterbrechen. Weil Sie diesen Längenwert auf Null setzen, sagen Sie ihm effektiv, dass er vergessen soll, dass er 1000 Elemente tragen soll. Außerdem darf D Speicher für zukünftige Anhänge reservieren, sodass verschachtelte dynamische Arrays möglicherweise nicht wie erwartet ausgerichtet sind.

Wenn Sie mehrdimensionale Arrays an C senden möchten, verwenden Sie am besten statische Arrays oder ein kontinuierliches dynamisches Array. Ihr Beispiel mit statischen Arrays:

void main() 
{ 
    auto a = new double[3][1000]; 
    a[0] = [1, 2, 3]; 
    a[1] = [3, 4, 5]; 
    double* p = &(a[0][0]); 
    for(size_t i=0; i<20; i++) { 
     writef("%.0f ", *(p+i)); 
    } 
    writeln; 
} 

Drucke

1 2 3 3 4 5 nan nan nan nan nan nan nan nan nan nan nan nan nan nan 

beachten, dass es ein Array von tausend [3] Arrays ist, so [0] und [1] angrenzen.

Bearbeiten Sie Ihre bearbeiten zu beantworten:

Sie haben zwei Möglichkeiten, die beide suchen identisch mit C.

// dynamic array of double[3] 
double[3][] a; 

// or one-dimensional array 
double[] a; 

a.reserve(1000); // ensure space for 1000 new elements (use 3000 for the second option) 

a ~= [3, 5, 7]; // works for both 
// pass to C using a.ptr 
+0

Diese Nullen sind nicht wirklich Nullen, sondern eher nicht initialisierte Werte - im Beispiel habe ich den Typ "double" verwendet, um zu sehen, welche Zahlen initialisiert wurden. Aber Sie haben recht, es war meine Zuweisung zu der Längeneigenschaft, die die Anfangskapazität veränderte, ich hielt es für selbstverständlich, dass es nicht so sein sollte. – Alchemit

+0

Danke, es funktioniert, obwohl ich es seltsam finde, dass die Reihenfolge der Indizes in 'new double [3] [1000]' im Vergleich zur C-Syntax 'double a [1000] [3]' wäre 1000 Zeilen. Eine weitere Frage: Wird dieses Array auf dem Heap oder auf dem Stack zugewiesen? – Alchemit

+0

Wie gesagt, die Nullen in Ihrem GDC-Beispiel sind der Längenwert und wirklich Nullen. Die Länge ist niemals uninitialisiert. Und es wird neben den Daten in dynamischen Arrays gespeichert. Was die Syntax angeht: Wenn 'double [3]' ein Array aus drei Doubles ist, sollte 'double [3] [1000]' ein Array aus tausend 'double [3]' s sein. Betrachten Sie es als "(doppelt [3]) [1000]". Ich würde die C-Syntax nicht zu sehr beibehalten, da sie dort nicht einmal neben dem Typ steht. – weltensturm

1

OK, dank @weltensturm und mit etwas mehr Tests und Lesen der Logik D hat endlich "geklickt", also fasse ich zusammen.

Grundsätzlich müssen alle Dimensionen außer dem äußersten statischen, das zur Kompilierungszeit bekannt ist, auf das mehrdimensionale dynamische D-Array-Layout im Speicher angewiesen sein. Diese Erklärungen:

double[3][] b;    // allocated on the stack, or 
auto b = new double[3][](0); // allocated on the heap 

beide verstanden als dynamische Anordnungen von (statische Arrays der Länge 3) und die Größe der Zeilen nicht mehr ändern (aber die Anzahl der Zeilen Ja):

b ~= [1,2,3];     // works 
b ~= [4,5,6,7.];    // Compilation error! 

Das ist die beste Garantie für Robustheit, die man bekommen kann.

Während also die Verwendung von Arrays deklariert als:

double a[];     // or 
double[][] a;     //, or 
auto a = new double[][](0,3); // these will be initial dimensions, not static! 

möglich ist, es bittet um Ärger weil D Laufzeit bei jeder Operation jede Dimension ändern, die den Inhalt des Arrays nicht ändern darf. Zum Beispiel mit einem der obigen Erklärungen ist es möglich, unebene Zeilen anfügen:

a ~= [1,2,3]; 
a ~= [4,5,6,7.];    // accepted 

und wenn es Speicher Neuzuweisung auslöst, wird es auch wahrscheinlicher, als nicht das Layout der Daten im Speicher zu ändern.

Als Randbemerkung, lustig ist der Compiler-Fehler dazu:

auto b = new double[3][]; 
Error: new can only create structs, dynamic arrays or class objects, not double[3][]'s 

, da es nicht ein dynamisches Array war, während dies:

auto b = new double[3][0]; 

akzeptiert wird und es macht eine Reihe mit statischer Anzahl von Spalten und dynamischer Anzahl von Zeilen. Nun ...