Kurze Frage über Klassen und listen



  • Fellhuhn schrieb:

    Was das Füllen angeht:

    #include <algorithm>
    //...
    fill_n(field[0], 81, 888);
    

    Igitt - das fällt definitiv unter undefiniertes Verhalten (du versuchst, ein 2D-Array als eindimensionale Datenanordnung zu betrachten, das kann schon in die Hose gehen, wenn der Compiler Hilfsmaßnahmen zur Überlauf-Kontrolle einbaut).



  • Der Compiler ist eh 'ne doofe Kuh. 😉

    Hm, sind die nicht per Definition hintereinander im Speicher abgelegt? Und welcher Compiler macht einen Überlaufschutz? Das verhindert doch die ganzen netten Spielereien.



  • Fellhuhn schrieb:

    Der Compiler ist eh 'ne doofe Kuh. 😉

    Ja, er setzt stur genau das um, was du ihm übergeben hast.

    Hm, sind die nicht per Definition hintereinander im Speicher abgelegt? Und welcher Compiler macht einen Überlaufschutz? Das verhindert doch die ganzen netten Spielereien.

    hintereinander ja, lückenlos hintereinander afaik nicht unbedingt (ich hab allerdings den Standard nicht hier, um das zu belegen).
    (und wer sich auf undokumentierte Features seines Compilers verlässt, lebt sowieso gefährlich)


  • Mod

    CStoll schrieb:

    Fellhuhn schrieb:

    Was das Füllen angeht:

    #include <algorithm>
    //...
    fill_n(field[0], 81, 888);
    

    Igitt - das fällt definitiv unter undefiniertes Verhalten (du versuchst, ein 2D-Array als eindimensionale Datenanordnung zu betrachten, das kann schon in die Hose gehen, wenn der Compiler Hilfsmaßnahmen zur Überlauf-Kontrolle einbaut).

    Welche Überlaufkontrolle? Das ist ganz normale Zeigerarithmetik, bei der jeder Zeiger innerhalb (bzw. 1 hinter dem Ende) des Arrays landet, folglich kein Überlauf.

    CStoll schrieb:

    hintereinander ja, lückenlos hintereinander afaik nicht unbedingt (ich hab allerdings den Standard nicht hier, um das zu belegen).
    (und wer sich auf undokumentierte Features seines Compilers verlässt, lebt sowieso gefährlich)

    Selbstverständlich lückenlos. Wie sonst könnte der Zugriff auf Arrayelemente durch einfache Zeigerarithmetik abgebildet werden?


    Übrigens erlaubt diese Arithmetik zusätzlich die Feststellung, dass sizeof(T) immer ein ganzzahliges Vielfaches des Alignments von T ist.

    Diese Form der Faltung von zwei Dimensionen in eine macht Arrays aus Arrays doch erst interessant (für gewöhnlich machen wir es anders herum), im Gegensatz zu der furchtbaren Variante mit **** können mehrdimensionale Arrays auf natürliche Weise (also ohne Adaptierung) als Sequenz ihrer Elemente verwendet werden und damit zusammen mit Standardalgorithmen. Daher ist das nicht einmal schlechter Stil (die Verwendung magischer Konstanten natürlich schon).



  • camper schrieb:

    CStoll schrieb:

    Fellhuhn schrieb:

    Was das Füllen angeht:

    #include <algorithm>
    //...
    fill_n(field[0], 81, 888);
    

    Igitt - das fällt definitiv unter undefiniertes Verhalten (du versuchst, ein 2D-Array als eindimensionale Datenanordnung zu betrachten, das kann schon in die Hose gehen, wenn der Compiler Hilfsmaßnahmen zur Überlauf-Kontrolle einbaut).

    Welche Überlaufkontrolle? Das ist ganz normale Zeigerarithmetik, bei der jeder Zeiger innerhalb (bzw. 1 hinter dem Ende) des Arrays landet, folglich kein Überlauf.

    Du hast aber nicht ein Array, sondern 9 einzelne Arrays mit jeweils 9 Elementen - und versuchst nun, 81 Werte in das erste dieser Arrays reinzuquetschen. Ich kenne die genaue Formulierung des Standards nicht aus dem Kopf, aber wie gesagt würde ich mich nicht darauf verlassen, daß du damit wirklich die Nachbar-Arrays triffst. (wenn du natürlich den Ansi-Standard zur Hand hast, kannst du mir gerne die exakte Formulierung als Beleg liefern)



  • Zwar kein Zitat aus dem Standard, da ich den gerade nicht zur Hand habe aber etwas gegoogel sagt lineare Speicherbelegung bei C und C++ und Pointer auf Arrays, sprich nicht lineare Spalten im Speicher, bei Java.

    Ist zwar nur irgend eine Seite aber schon einmal ein Hinweis, vielleicht in die richtige Richtung. 😉



  • Könntest Du den Hinweis auch noch verständlich formulieren? Für mich klingt der Satz eher wirr.



  • Wollte sagen: In C und C++ liegt ein Array, egal wieviele Dimensionen, im Speicher hintereinander. Bei Java stehen im Speicher die Adressen der folgenden Dimensionen im Speicher. Also bei Java verteilt, bei C und C++ linear/zusammenhängend.


  • Mod

    Was den inneren Aufbau von Arrays angeht, Abschnitt 8.3.4 des Standards lesen, u.a.

    An object of array type contains a contiguously allocated nonempty set of N sub-objects of type T.

    Interessanter und nicht ganz so leicht zu beantworten (dafür evtl. fernliegender) ist die Frage, ob nicht die Pointeraddition deshalb undefiniert ist, weil zwar Operand und Ergebnis Zeiger auf (bzw. in) das gleiche Objekt sind, der Pointeetyp aber nicht der Elementtyp des Arrays, auf das wir uns dabei beziehen:

    5.7/5 expr.add schrieb:

    When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integral expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and i–n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

    Nimmt man das alles wörtlich und ignoriert auch noch den Rest des Standards, könnte man tatsächlich auf die Idee kommen, dass so etwas undefiniert bleibt. Wobei undefiniert ggf. nur heißt: nicht hier definiert - aber möglicherweise für bestimmte Fälle an anderer Stelle des Standards. Typisches Beispiel etwa findet man bei reinterpret_cast, dass im Allgemeinen implementation-defined bleibt, nicht aber im Falle von union-Membern. Was auf jeden Fall nach diesem Absatz möglich ist, ist ein sequentieller Zugriff auf aufeinanderfolgende Einzelelemente, denn one-past-the-last-element des einen Subarrays ist ja gleichzeitig das erste Element des nächsten Subarrays (siehe 8.3.4). Ich denke, der Abschnitt wäre klarer, wenn im letzten Satz statt "elements of the same array object" stünde: "elements of the same complete object". Denn das konkrete Array mag im Zweifel gar nicht zu ermitteln sein. Etwa in

    union
    {
        T x[n][m];
        T y[n*m];
    };
    

    korrespondiert jedes Element (bzw. Subelement) des einen Arrays mit einem Element des Anderen - &x[a][b] = &y[a*m+b] - wenn wir aber nicht das jeweilige Array mit Sicherheit bestimmen können, dann muss jedes denkbare Array an dieser Stelle ein Array im Sinne von 5.7/5 sein, und es genügt eines, für das die das Ergebnis definiert ist, damit die Operation wohldefiniert ist. Einen anderen Grund finden wir in 1.7 und 1.8

    The memory available to a C + + program consists of one or more sequences of contiguous bytes.

    An object is a region of storage.

    Ein Objekt besteht also aus einer Anzahl (lückenlos) aufeinanderfolgender Bytes. Mit anderen Worten: es ist möglich, den Speicher eines Objektes als ein array aus chars/unsigned chars zu behandeln - das impliziert Pointerarithmetik mit char-Zeigern, obwohl das Objekt selbst im Allgemeinen ja nicht explizit als char-Array deklariert wurde. Dann ist aber schwer einzusehen, warum dies nicht auch für andere Typen als char gelten soll (natürlich müssen zusätzliche Voraussetzungen, wie Ausrichtung, Lebensdauer, Aliasing berücksichtigt werden, aber das berührt die Pointeraddition als solche nicht), zumal auch Fußnote 75 (zwar nicht normativ) Pointerarithmetik über char-Zeiger definiert.
    Nicht zuletzt überlegen wir noch, wie es ist, wenn wir ein Array dynamisch ohne new allokieren:

    T* p = (T*)malloc(n*sizeof(T));
    

    Natürlich können wir hier mit p Pointerarithmetik in den Grenzen des allokierten Speichers benutzen - formal wurde ein Array aus T aber dort nie deklariert.



  • Danke erstmal für die Vielen Antworten - und vor allem an den 2.Beitrag (von Queer_boy) 🙂 ! Das mit der Funktion geht jetzt, leider lässt sich von dem ganzen geposte ableiten, dass das Füllen etwas schwieriger ist, als ich vorerst dachte!

    Hm, sind die nicht per Definition hintereinander im Speicher abgelegt? Und welcher Compiler macht einen Überlaufschutz? Das verhindert doch die ganzen netten Spielereien.

    Ich verwende den Dev C++ Compiler, den ich eigentlich ganz gut finde 😉 !


Anmelden zum Antworten