Weiß jemand, warum die Weite der "zweite" Arraydimension im formalen Parameter benötigt wird?



  • Ich frage mich gerade, warum ich bei der Methodendeklaration eigentlich die Größe der inneren Arrays angeben muss?
    Oder anders gefragt, an welche Stelle eines Zellzugriffs in der Funktion muss davon ausgegangen werden, dass die inneren, linearen Arrays die angegebene Länge haben?

    Parallelpost: http://cboard.cprogramming.com/c-programming/159863-why-width-second-array-dimension-neccessary-parameter.html



  • Was soll eine Methodendeklaration sein? Hab gar nicht mitbekommen, dass man in C OOP eingeführt hat.

    Du solltest dich eher fragen, warum die erste Dimension weggelassen werden darf... das ist so, weil ein mehrdimensionales Array nichts weiter als ein eindimensionales Array ist, das als Elemente wiederum Arrays enthält, und die Größe von (eindimensionalen) Arrays in Parameterdeklarationen weggelassen werden kann (sie wird sogar ignoriert, ein Array-Parameter ist ausnahmsweise mal dasselbe wie ein Zeiger). Die inneren Dimensionen dürfen nicht weggelassen werden, weil sonst die Größen der Array-Elemente nicht bekannt wären. Stell dir vor, du hast ein Array [3][2]:

    { {0, 1}, {2, 3}, {4, 5} }

    Wenn du die äußere Dimension nicht hättest, wär das nicht weiter schlimm, sofern du sie irgendwie (Zusatzparameter) in Erfahrung bringen könntest. Du kannst jedes Unter-Array ansprechen, und deren Elemente auch wieder.

    Wenn du die innere Dimension nicht hättest, würdest du (bzw. der Compiler) nicht wissen, wie du vom ersten zum zweiten und zu jedem weiteren Unterarray kommst.



  • **(arr + 1) greift auf den ersten Wert des zweiten inneren Arrays zu, ohne, dass irgendwo die Länge verwendet wird. Das ist möglich, da unter *(arr + 1) ein Pointer auf das erste Element des zweiten inneren Arrays existiert, wie unter *(arr + 2) ein Pointer auf den ersten Wert das dritte innere Arrays liegt.


  • Mod

    Der wichtige Erkenntnis, die hier fehlt (dir, Bashar, ist das so etwas von selbstverständlich, dass du gar nicht da drauf kommst, dass jemand das auch anders sehen könnte) ist, dass Arrays nichts sind außer eine Ansammlung von Objekten des gleichen Typs, alle einfach hintereinander in Reihe. Da sind keinerlei Verwaltungsdaten oder ähnliches beteiligt. Ein Array weiß nichts wie groß es ist oder welchen Typ seine Elemente haben. Es besteht nur aus seinen Nutzdaten und sonst nichts.
    Der Compiler kann aber Code erzeugen, der auf diesen Daten arbeitet, wenn er nur weiß, wie groß die Datenelemente sind. Und im Idealfall weiß er sogar wie groß das ganze Array ist, wobei er diese Information jedoch nur für sehr wenige Anwendungen benötigt (z.B. sizeof, daher funktioniert sizeof(zeiger auf array) nicht so, wie Anfänger es oft erwarten, das gibt nämlich die Größe eines Zeigers). Für den Zugriff braucht es aber auf jeden Fall die Größe der Datenelemente, daher muss deren Typ bekannt sein. In Bashars Beispiel wäre der Typ der Datenelemente des äußeren Arrays int[2]. Daher weiß der Compiler, wenn man auf Index N des äußeren Arrays zugreifen will, dann muss er die Startadresse + N mal die Größe eines Elements nehmen.



  • Nein, es gibt dazwischen noch eine Ebene, die Pointer auf die ersten Elemente aller inneren Arrays enhält. Somit ist es nicht nötig, die innnere Breite mit dem Typ zu multiplizieren, da auf die jeweiligen "Grenzen" bereits Pointer existieren.


  • Mod

    rednaZ schrieb:

    Nein, es gibt dazwischen noch eine Ebene, die Pointer auf die ersten Elemente aller inneren Arrays enhält. Somit ist es nicht nötig, die innnere Breite mit dem Typ zu multiplizieren, da auf die jeweiligen "Grenzen" bereits Pointer existieren.

    So so. Und wie kommst du da drauf, dass das so wäre? Ist es nämlich nicht. Ein Array ist kein Pointer, ein Pointer ist kein Array. Und ganz besonders ist ein 2D-Array kein Array von Pointern! Du setzt gerade zwei ganz unterschiedliche Strukturen gleich:
    http://www.panix.com/~elflord/cpp/gotchas/index.shtml



  • [quote="rednaZ"]**(arr + 1) greift auf den ersten Wert des zweiten inneren Arrays zu, ohne, dass irgendwo die Länge verwendet wird. Das ist möglich, da unter *(arr + 1) ein Pointer auf das erste Element des zweiten inneren Arrays existiert, wie unter *(arr + 2) ein Pointer auf den ersten Wert das dritte innere Arrays liegt.[/quote]



  • Da ich mit **(arr + 1) auf den ersten Wert des zweiten inneren Arrays zugreifen kann, muss es einen Pointer *(arr + 1) geben, der auf diesen Zeigt.
    Und auf diesen Pointer liegt eine Speicherstelle weiter als der Pointer auf den »arr« Zeigt, denn »arr« zeigt auf den Pointer, der auf das erste Element des ERSTEN inneren Arrays zeigt.



  • Wen du ein echtes 2D-Array meinst

    int feld[3][2] = { {0, 1}, {2, 3}, {4, 5} } ;
    

    Dann sind da nur Nutzdaten. Nimm einen Debugger und schau es dir an.

    Der Compiler macht bei einem Zugriff darauf aus dem feld[i][j] ein (feld+i2+j)
    Wie du siehst, ist darin die 2 zur Berechnung des Indizes enthalten.
    Die Größe des höchsten Index spielt dagegen keine Rolle. Wenn du den falsch angibst, ist das dein Problem.
    Wie man auch sehen kann, ist das auch nur ein einfacher Zeiger.

    Der Compiler kennt im Scope von feld die genaue Größe und kann daher auch Zwischenzeiger generieren. Diese sind aber nirgends gespeichert.



  • Um es nochmal deutlich zu machen:
    Ein 2D Array ist ein 1D Array!
    Aus array[i][j] *(array + i * columns + j).
    Dafür braucht der aber columns, sonst geht die Berechnu.g nicht.
    Und deshalb musst du sie angeben.



  • Ach so einer bist du. Ich will 10 Minuten meines Lebens zurück! 😞



  • Danke DirkB, die Unterscheidung zwischen Stack und Heap muss es sein.



  • Was für einer ist "so einer"?



  • Nein, Stack und Heap sind C völlig egal.

    Und bei

    int zeile1[2] = {0,1};
    int zeile2[2] = {2,3};
    int zeile3[2] = {4,5};
    
    int feld *[3];
    feld[0] = zeile1;
    feld[1] = zeile2;
    feld[2] = zeile3;
    

    ist auch kein Heap im Spiel.
    Hier sind richtige Zeiger für die Zeilen im Spiel.
    feld ist hier kein echtes 2D-Array, auch wenn beim Zugriff auf die Elemente die gleiche Schreibweise benutzt wird.



  • Bashar schrieb:

    Ach so einer bist du. Ich will 10 Minuten meines Lebens zurück! 😞

    Dito.



  • Mit der Unterscheidung von Stack und Heap hab' ich gemeint, was ich unter "Der Compiler kennt im Scope von feld die genaue Größe und kann daher auch Zwischenzeiger generieren. Diese sind aber nirgends gespeichert." verstanden habe.
    Dein Code funktioniert leider nicht. Ich glaub' der Indirektionsstern muss vor »feld«, aber auch dann versteh' ich's nicht ganz, muss es jetzt aber auch dabei belassen und später noch mal drüber nachdenken.
    Guten Abend.



  • Ja klar muss das

    int *feld[3];
    

    sein. 🙄



  • rednaZ schrieb:

    Was für einer ist "so einer"?

    Sorry, ich reg mich wahrscheinlich mehr darüber auf als gerechtfertigt ist. Ich war mit 12 auch so, aber da gabs nur Lehrer als Opfer und kein Internet... (Falls du absolut keine Ahnung hast, worum es geht: Deine Entgegnung auf die gegebenen Antworten, die mit "Nein, ..." anfängt.)


  • Mod

    @rednaZ:

    int foo(void) {}
    int main()
    {
        foo(); // klar
        (*foo)(); // ?
        (******************************foo)(); // ???
    }
    

    stauen, verstehen und dann zu der Frage mit den Arrays zurückkehren.



  • Zeiger auf Funktionen (gleichbedeutend mit Funktionsnamen) sind völlig anders zu behandeln und inkompatibel zu Datenzeigern.
    Demzufolge ist auch die Dereferenzierung via '*' anders zu betrachten und ergibt (sinnigerweise) dann auch unterschiedliche Ergebnisse.


Log in to reply