int **x, int *x[] und int x[][]



  • Hallo,

    ich habe immer angenommen, dass
    int **x; und int *x[]; und int x[][];
    immer das selbe bedeutet;
    so als würden die eckigen Klammern nur den Pointer ersetzen.
    Das stimmt ja aber doch nicht, oder?
    Könnte mir das vielleicht jemand erklären?

    mfg DerUnterschied



  • int** x;   // <- Pointer auf einen Pointer auf einen int
    int* x[];  // <- Array unbestimmter Größe aus Pointern auf int
    int x[][]; // <- error, nur die letzte Ebene kann unbestimmte Größe haben
    


  • Danke dot.

    Was würde

    int ***x;
    

    dann für ein Sinn machen? Vor deiner Antwort habe ich gedacht, dies sei 1zu1 das selbe wie int x[][][];
    Nach deiner Erklärung ist es aber ein
    Pointer -> Pointer -> Pointer -> int.
    Die ersten "zwei Ebenen" zeigen dabei doch auch wieder nur auf ein Pointer.
    "Die int-Adresse eines Pointers der auf ein Pointer" zeigt müsste doch aber genau so groß sein wie "die int-Adresse eines Pointers der auf ein Pointer zeigt der wiederrum auf ein Pointer zeigt".
    Also sizeof(**int) == sizeof(*int);
    Demnach müsste doch auch int *x == int **x; sein?

    Egal wie viele * ich noch vor die Variable packe:
    x zeigt immer noch auf ein Pointer (sofern * > 2).

    int ***x;
    und
    int **y, *x;
    x = &y;

    ist das selbe?
    Wenn also das * keine gleichbedeutende Alternative zu [] ist:
    Warum sieht man ab und an so eine schreibweiße wie int ***foo; ?

    Ich hoffe, man versteht was ich meine.



  • Worauf ich zudem hinaus möchte:

    Egal wie viele * ich noch vor die Variable packe:
    x zeigt immer noch auf ein Pointer (sofern * > 2).

    Die benötigte Speichergröße bleibt doch konstant?



  • Ich denk, all deine Verwirrung stammt daher, dass du dem weit verbreiteten Irrglauben unterliegst, dass Arrays irgendwie nichts anderes seien als Pointer. Dem ist aber ganz und gar nicht so. Einfacher Beweis:

    int* a;
    int b[12];
    
    std::cout << sizeof(a) << " vs. " << sizeof(b) << '\n';
    

    😉

    a in diesem Beispiel ist ein Pointer auf einen int . b dagegen ist ein Array aus 12 int s. b ist nicht nur ein Zeiger auf einen Speicherbereich, der 12 int s enthält. b ist der Speicherbereich. b kann lediglich implizit in einen Pointer auf den ersten int im Array konvertiert werden, wenn es in einem Kontext verwendet wird, der einen Pointer erfordert.

    DerUnterschied schrieb:

    Was würde

    int ***x;
    

    dann für ein Sinn machen? Vor deiner Antwort habe ich gedacht, dies sei 1zu1 das selbe wie int x[][][];
    Nach deiner Erklärung ist es aber ein
    Pointer -> Pointer -> Pointer -> int.

    Ganz genau das ist es auch.

    DerUnterschied schrieb:

    Wenn also das * keine gleichbedeutende Alternative zu [] ist:
    Warum sieht man ab und an so eine schreibweiße wie int ***foo; ?

    Wenn foo ein Array ist, kann es in einen Pointer sein erstes Element konvertiert werden. *foo wäre dann das erste Element des Arrays foo . Wenn foo ein Array aus einem Array ist – und z.B. int x[3][4] ist nichts anderes als ein Array aus 4 Arrays aus 3 int s) – dann zeigt *foo auf das erste Element des Arrays, welches selbst wiederum ein Array ist und in einen Zeiger auf sein erstes Element konvertiert werden kann. **foo zeigt dann auf das erste Element des ersten Elements usw. All das funktioniert nur, weil die Arrays, mit denen du arbeitest, überall implizit in Zeiger auf ihre ersten Elemente konvertiert werden. Dabei wird ein temporärer Zeiger erstellt, der auf das erste Element zeigt. Das jeweilige Array selbst ist und bleibt ein Array und kein Pointer...



  • Welche Ausgabe ich da erhalte war mir auch klar.
    Irgendwie hat es aber immer noch nicht "klick" gemacht.

    Kannst du mir sagen wann ich eine Funktion so aufbaue:

    int foo(int *a);
    

    und wann so:

    int[] bar(int a[]);
    

    ?

    int x = { 1, 2, 3 };
    foo(&x);
    bar(x);
    

    Die eine könnte ein Call by reference werden, die andere ein Call by value. Ok.

    Ach, vielleicht sollte ich auch mehr programmieren statt mich soviel mit der Theorie zu beschäftigen. Womöglich wird es mir dadurch klarer.

    Danke jedenfalls schonmal für deine Hilfe 🙂

    PS: std::cout 😡 ... 😉 @ C (C89, C99 und C11)



  • Das Problem mit Funktionen ist, dass es in C und C++ arkane Regeln gibt, die besagen, dass Arraydeklarationen in der Deklaration einer Parameterliste einer Funktion keine Arrays deklarieren, sondern Pointer (genaugenommen werden die Deklarationen vom Compiler angepasst). Wenn du schreibst void f(int x[]) , dann ist das erste, was der Compiler macht, wenn er diese Deklaration sieht, dass er sie umwandelt in void f(int* x) .

    void f(int x[]);
    void f(int* x);
    

    deklariert also zweimal die selbe Funktion. Das liegt aber nicht daran, dass int x[] das gleiche wäre wie int* x , sondern daran, dass der Compiler in diesem Fall still und heimlich deinen Code umschreibt. Der Effekt davon ist, dass es so aussieht, als ob Arrays in C, im Gegensatz zu allen anderen Typen, "by Reference" übergeben würden; denn wenn du diese Funktion nun mit einem Array aufrufst, wird das Array implizit in einen Pointer auf das erste Element umgewandelt, da der erste Parameter der Funktion ja ein Zeiger ist.

    Zusammenfassung: Pure Arrays sind in C wahrlich merkwürdige Gebilde, die auf den ersten Blick vielleicht anmutig und zahm erscheinen mögen, in Wahrheit aber teilweise extrem obskuren Regeln folgen. Wenn du C++ nutzen würdest, würde ich an dieser Stelle empfehlen, std::array zu verwenden, denn das verhält sich – im Gegensatz zu puren Arrays – so, wie man es intuitiv erwarten würde. So empfehle ich halt einfach, C++ zu verwenden... 😉

    Btw: Eine Funktion kann kein Array als Rückgabewert haben.



  • Hehe.
    Danke für deine Erklärungen.
    Ich denke, mir ist einiges klarer geworden 🙂


Anmelden zum Antworten