C Programmierung: Zweidimensionale Arrays und Pointer?

3 Antworten

Die Syntax deiner Zuweisungen ist etwas eigenartig. Meiner Meinung nach sollt hier

int (*p1)[3]= ary[0]; //Die ersten 3 Byte im Array ary
int (*p2)[2][3] = ary;

stehen. Dem Compiler wirds egal sein, weil hier Arrays ohnehin immer zu pointern werden, das & machst du dazu um zu zeigen, dass es ein Doppelpointer für die beiden Dimensionen ist. Aber Syntaktisch ist es meiner Meinung nach so richtig. Die Zuweisung von ary alleine Funktioniert eben weil ary am Ende auch nichts anderes als ein Pointer auf das erste Element ist, aber der Compiler unterscheidet idR zwischen Arrays und Pointern.

int[2][3] *p2

Es gibt auch sprachen die es so schreiben, ist eben nur keine C Syntax.

int (*p2)[2][3] bedeutet ich habe einen Pointer auf den Datentyp int der in Form eines Zweidimensionalen Arrays zu interpretieren ist.

return Value oder einen Funktionsprototypen aus einem Pointer zu einem zweidimensionalen Arrays (mit klar definierter Größe) haben möchte?

Ich weiß hier nicht genau was du meinst, aber C gibt keine Arraygrößen bei der Rückgabe eines Pointers aus. Du musst entweder eine Struct zurückgeben wo die Dimensionen stehen oder du gibst sie als zusätzliche Parameter zurück, aber ein Pointer alleine hat in C keine Dimensionen.

AOMkayyy 
Fragesteller
 28.12.2021, 20:49

Danke für die Antwort!

Dem Compiler ist es leider nicht egal, der Unterscheidet zw verschiedenen Pointern, aber anfangs hatte ich genau die selbe Ansicht wie du (also wenn ich dich richtig verstehe). Jetzt denke ich, dass die C Syntax bei der Zuweisung durchaus einleuchtend ist, aber trotzdem etwas kompliziert.

Der Grund warum man es so schreibt ist m.M.n. das ary direkt auf das erste Element verweist und das ist hier ein int Array der Länge 3, ary[0] widerum verweist auf das erste Element in diesem int Array der Länge 3 (ist also vom Typ int Pointer und nicht "int Array der Länge 3" Pointer) und &ary verweist auf ein 2 dim. int Array mit besagten Maßen.

Mit "return value und prototype" Frage meinte ich, wenn ich nun so einen Pointer habe, wie int (*p2)[2][3] = ary; , wie gebe ich ihn in einer Funktion ordnungsgemäß zurück? Das kann man natürlich machen, in dem man einen Parameter in die Funktion übergibt und es da zuweist, aber ich meine wirklich als return Wert. Normal schreibt man ja z.B. int Funktion() {bla; int x = 4; bla; return x;} aber was setze ich jetzt vor dem Wort "Funktion" anstatt int ein?

int (*)[2][3] Funktion() geht glaube ich leider nicht.

Nochmals danke für die Antwort!

0
PeterKremsner  28.12.2021, 21:01
@AOMkayyy

ary ist an sich das Array mit zwei Dimensionen.

Ary[0] ist das Subarray mit 3 Einträgen an der Stelle 0. Ary[1] wäre das zweite Subarray mit 3 einträgen.

Ary = Ary[0] ist weil es in diesem Fall dem Compiler egal ist ob du von einem Pointer ausgehst oder einem Array das Array degeneriert eben zum Pointer in diesem Fall. Ary[0] wählt hingegen wirklich das Subarray mit dem Index 0 aus.

&ary oder ary sollte in diesem Fall Syntaktisch gleich sein. &ary macht eben aus dem Array explizit einen Pointer auf die Basis ary macht das implizit.

Du kannst bei der Funktion nur int * zurückgeben. Du kannst ebene nur den Pointer zurück geben nicht aber die Dimensionen von diesem.

Genau genommen sind 2 Dimensionale Arrays in C ja auch nur syntaktischer Zucker. Also das Array arr[][] = {{1,2,3},{4,5,6}} ist im Speicher das selbe wie das Array arr[]={1,2,3,4,5,6}. Du kannst also jedes Zweidimensionale Array am Ende auch wie ein eindimensionales Behandeln, darum genügt die Rückgabe von int * damit du auch mehrdimensionale Arrays behandeln kannst.

1
AOMkayyy 
Fragesteller
 29.12.2021, 01:18
@PeterKremsner

Ich bedanke mich! Trotzdem glaube ich, dass die Unterscheidung, zumindest bei gcc, doch stärker ausfällt. (Kann auch sein, dass wir aneinander vorbeisprechen) Ary = Ary[0] stimmt im Bezug auf die reine Speicheradresse, aber der Compiler unterscheidet, denn Ary[0] (auch wenn er für uns den 0ten Subarray als solches repräsentiert) zeigt eigentlich auf das erste Element des 0ten Subarrays, ist also ein ganz normaler int Pointer, Ary widerum wird vom Compiler als int-pointer Pointer angesehen, somit sind sie nicht kompatibel. &Ary widerum ist ein int** Pointer (bzw. angepasst auf die Maße des Arrays), verweist aber ebenfalls auf die selbe Speicheradresse, kompatibel sind aber alle drei nicht. Zumindest ist das meine Erfahrung nach einigem rumprobieren.

0
PeterKremsner  29.12.2021, 12:54
@AOMkayyy

Ich hab mir das jetzt mit den 2D Arrays nochmal angesehen und bin drauf gekommen, dass die Array notation nicht ganz passt.

int ary[2][3] = {
        { 1, 2, 3 },
        { 4, 5, 6 }
    };
    int *p1 = ary[0]; //Pointer zum ersten Subarray
    int *p2 = ary[1]; //Pointer zum zweiten Subarray
    int(*p3)[3] = ary; //Pointer auf Subarrays der länge 3
    int *p4 = &ary[0][0];    //geht auch nutzt aber aus, dass solche Arrays immer ein zusammenhängender Speicherbereich sind

Das wäre jetzt die Notation die ich gemeint habe.

int (*p2)[2][3] = &ary;

Wäre natürlich ein Pointer auf ein Array mit den Maßen [2][3] also eigentlich ein int ***. Sprich wenn du diesen Pointer verwendest müsstest du ihn dereferenzieren also:

for (int i = 0; i < 2; i++)
{
    for (int d = 0; d < 3; d++)
    {
        printf("%d", (*p2)[i][d]);    
    }
}

int (*p2)[2][3] wäre in dieser Notation nämlich ein 3facher Pointer und natürlich nicht mehr gleich dem Array. Daher musst du auch das & vor ary schreiben weil du nicht ary in diesem Pointer haben möchtest sondern eben den Pointer zu ary.

Ich hab dir hier mal ein Testprogramm zusammengestellt:

https://onlinegdb.com/OV0M2unBW

Da sind auch die Beispiele drinnen wie du zB mit malloc in einer Funktion ein 2D Array erzeugst. Einmal ein echtes 2D Array und einmal ein Array welches zwar genutzt wird wie ein 2D Array aber eigentlich keines ist.

1
int (*p1)[3]

Meiner Auffassung nach ist das kein Pointer auf ein Array der Länge 3, sondern ein Array mit Platz für 3 Pointer vom Typ int.

Du hast also keinen Pointer erzeugt, sondern nur ein Array wo pointer Platz drin hätten.

ary und &ary sind exakt die selben Pointer.

Das sind überhaupt gar keine Pointer.

Das ist einmal dein Array und einmal die Startadresse deines Arrays.

In einer Array-Deklaration kannst du nicht in der Reihenfolge "Typ Größe, Variablenname" vorgehen, ist also einfach falsche Syntax.

Wenn du einen Pointer auf das Array haben möchtest, machst du einfach

int *p1 = arr; , alternativ auch int*p1 =&arr[0];

Hier mehr zu dem Thema

PeterKremsner  28.12.2021, 18:37

Das erste stimmt schon so.

Die Klammer ist genau darum da.

int (*p1)[3] //pointer auf ein integer Array mit der Länge 3

int *pi [3] //Array von 3 integer Pointern
0
AOMkayyy 
Fragesteller
 28.12.2021, 18:55

Also das mit dem Pointer müsste so schon stimmen, also das ( int (*p1)[3] ) ist schon ein einzelner Pointer auf einen int Array der Länge drei. Was du meinst wäre int *p1[3], das wäre nun ein Array der Länge 3 aus int Pointern.

Naja, ich stimme dir zu, dass es vielleicht strikt genommen kein Pointer ist (da man z.B. nicht inkrementieren kann), jedoch sind beides Speicheraddressen bzw. sie zeigen auf einen Platz im Speicher und zwar den selben.

Ich möchte auch keinen Array deklarieren, sondern einen Pointer auf einen 2 dimensionalen Array erzeugen (was ich da oben auch tue).

Wenn du einen Pointer auf das Array haben möchtest, machst du einfach
int *p1 = arr; , alternativ auch int*p1 =&arr[0];

Das Funktioniert bei eindimensionalen Arrays, aber nicht mehr bei Mehrdimensionalen. Der Code würde bei meinem Array so einen Fehler schmeißen, weil das komplett unterschiedliche Typen von Pointern sind, ich könnte lediglich int*p1 = arr[0] verwenden, dann wäre ich aber nur in einem der Unterarrays drin. Klar, damit könnte ich, durch die Art und Weise wie mehrdim Arrays im Speicher abgelegt werden, durch die Arrays navigieren, aber nicht annähernd so komfortable wie mit Indizes z.B. p1[3][4] und der Pointer wäre nicht auf das Array, sondern nur auf ein Subarray gerichtet.

0
MobileAmigo  28.12.2021, 20:18
@AOMkayyy

Bei mehrdimensionalen arrays nutzt du einfach pointer auf pointer.

Also bei 2D einfach doppelpointer nehmen

0
AOMkayyy 
Fragesteller
 28.12.2021, 20:37
@MobileAmigo

Das hatte ich ursprünglich auch versucht und es ging leider nicht, vermutlich weil der Compiler die Länge der Subarrays braucht, damit er weiß, wieviele Bytes zwischen den Subarrays liegen.

0

Biestiger Themenbereich.

Schon Pointer und Arrays unterscheiden sich - und doch irgendwie nicht.

Die Reihenfolge der Angaben wird nunmal von der Spezifikation festgelegt und daran ist nicht zu rütteln, das muß man einfach so hinnehmen.

int (*p1)[3]

könnte man übersetzen als:

declare p1 as pointer to array 3 of int

Aber machen wir es doch anders:

int p1[3]  /* declare p1 as array 3 of int */
int *p1[3] /* declare p1 as array 3 of pointer to int */
int* p1[3] /* hier wirds deutlicher ? */

Bookmarke Dir vielleicht mal: https://cdecl.org/

ary und &ary sind exakt die selben Pointer

Nein, sie ergeben die gleiche Adresse, sind aber trotzdem unterschiedlich.

WUT?

Einfach lesen/probieren:

https://www.geeksforgeeks.org/whats-difference-between-array-and-array-for-int-array5/

AOMkayyy 
Fragesteller
 28.12.2021, 20:51

Danke dir!

Nein, sie ergeben die gleiche Adresse, sind aber trotzdem unterschiedlich.

Ja, ich hatte in meiner Frage auch in einem Absatz erläutert, warum ich denke, dass sie unterschieden werden, mit dem "sind gleiche Pointer" meinte ich auch, dass sie zur selben Adresse verweisen.

Hast du vielleicht noch eine Ahnung, wie man sowas in einer Funktion als Rückgabewert formuliert? Also wirklich mit einer Rückgabe durch return und nicht irgendwie durch einen Parameter.

0
KarlRanseierIII  28.12.2021, 21:37
@AOMkayyy

Also, machen wir es exemplarisch inklusive compiler-Warnungen, damit es klar wird:

int  f(void){
    int arr[2][3];
    return arr;
}
warning: returning 'int (*)[3]' from a function with return type 'int' makes integer from pointer without a cast

Beachte den vom Compiler ermittelten Rückgabewert.

int  f(void){
    int arr[2][3];
    return &arr;
}
warning: returning 'int (*)[2][3]' from a function with return type 'int' makes integer from pointer without a cast

Hier nochmal der ausdrückliche Unterschied bezüglich des & Operators - der Typ ändert sich.

Jetzt wird es lecker:

int  ( *f(void) )[2][3]{
    int arr[2][3];
    return &arr;
}
warning: function returns address of local variable

Der Compiler weist auf das Scoping-Problem hin.

CDECL sagt übrigens zu der Funktionsdefinition:

declare f as function (void) returning pointer to array 2 of array 3 of int.

Aber gerade wegen der Gültigkeitsgeschichte macht man das eher ungern.

1
AOMkayyy 
Fragesteller
 29.12.2021, 01:08
@KarlRanseierIII

Vielen Dank!

Das Problem mit dem Scope ließe sich ja theoretisch durch malloc/calloc umgehen, oder? Mir gehts ansich auch nicht so wirklich um die Anwendung, ich habe nicht vor das zu nutzen, aber ich würde dennoch gerne wissen wie es funktionieren würde.

0
KarlRanseierIII  29.12.2021, 01:09
@AOMkayyy

Ja, zum Beispiel.

Oder aber das Array (in der Funktion) müßte static sein, sodaß es über die gesamte Laufzeit existiert.

1