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


  • 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.


  • Mod

    anders wie?



  • Wutz schrieb:

    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.

    Das ist natürlich nicht korrekt. Der Typ einer Funktion ist kein Zeigertyp.

    ISO/IEC 9899:1999 6.9.1 (2) schrieb:

    The identifier declared in a function definition (which is the name of the function) shall have a function type, as specified by the declarator portion of the function definition.

    und

    ISO/IEC 9899:1999 6.2.5 (1) schrieb:

    The meaning of a value stored in an object or returned by a function is determined by the type of the expression used to access it. (An identifier declared to be an object is the simplest such expression; the type is specified in the declaration of the identifier.) Types are partitioned into object types (types that fully describe objects), function types (types that describe functions), and incomplete types (types that describe objects but lack information needed to determine their sizes).

    Es wäre ja auch nicht logisch. Wenn Funktionen Zeigertypen hätten, welchen Typ hätte das, worauf sie zeigen? (********f)(); liefe dann jedenfalls nicht mehr.

    Wie Arrays zerfallen Funktionen implizit in Zeiger, aber wo Arrays in Zeiger auf ihr erstes Element zerfallen, zerfallen Funktionen in Zeiger auf sich selbst:

    ISO/IEC 9899:1999 6.3.2.1 (4) schrieb:

    A function designator is an expression that has function type. Except when it is the operand of the sizeof operator) or the unary & operator, a function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’.



  • @TE: Dass

    #include <stdio.h>
    
    int main(void) {
      int arr[][3] = {
        { 1, 2, 3 },
        { 4, 5, 6 },
        { 7, 8, 9 }
      };
    
      int i;
      for(i = 0; i < 9; ++i) {
        printf("%d", arr[0][i]);
      }
      putchar('\n');
    
      return 0;
    }
    

    "123456789" ausgibt, sollte Beweis genug sein, dass dieser Zeigerindirektionsunfug stumpf nicht wahr ist. Ein zweidimensionales Array ist ein Array von Arrays, nicht ein Array von Zeigern. Jedes dieser Arrays zerfällt bei Bedarf in einen Zeiger auf sein erstes Element -- arr in einen int ()[3], arr[0], arr[1] und arr[2] in int, aber deswegen sind sie jeweils keine Zeiger.

    Dass die inneren Dimensionen bei der Zeigerübergabe angegeben werden müssen, liegt daran, dass die inneren Dimensionen in der Funktion dazu gebraucht werden, die Speicherposition eines Elementes bei der Dereferenzierung zu errechnen. arr[i][j] ist letzendlich nur arr[0][i * DIMENSION + j].


Anmelden zum Antworten