2017-09-03 1 views
1

Wenn ich ein mehrdimensionales Array wie folgt habe:Multi dimensionale Array Referenz in struct in c

int arr2d[2][3] = {{0,1,2}, {10,11,12}}; 

ich es auf eine Funktion, wie dies passieren kann:

void foobar(int arg[][3]) 

Dies ist kein Anruf durch value, das ist call by reference, also nur ein Zeiger auf die Startadresse, aber der Compiler weiß immer noch, dass es sich um ein 2D-Array handelt und ich kann darauf wie in der Funktion zugreifen.

Wie funktioniert das gleiche in einer Struktur?

typedef struct { 
    int arr2d[][3]; 
} Foobar_t 

Zuerst gibt mir das: error: flexible array member in otherwise empty struct. Ich kann das beheben:

typedef struct { 
    int dummy; 
    int arr2d[][3]; 
} Foobar_t 

Es wird ohne Fehler oder Warnungen kompilieren. Aber wenn ich versuche, es zu benutzen wie Foobar_t foobar = {1337, arr2d} ich einige Warnungen erhalten:

missing braces around initializer 
initialization makes integer from pointer without a cast 

Und wenn es den Zugriff auf: subscripted value is neither array nor pointer nor vector.

Eindimensionale Arrays können leicht als Zeiger behandelt werden. Bei mehrdimensionalen Arrays muss der Compiler jedoch die Größe der verschiedenen Dimensionen kennen, um die Offsets korrekt berechnen zu können. Gibt es einen Weg ohne Cast (int (*)[3]) und warum unterscheidet sich die Syntax von dem Funktionsparameter?

Das ist also die Work-around möchte ich vermeiden:

#include <stdio.h> 

static int testArr[2][3] = {{0,1,2},{10,11,12}}; 

typedef struct { 
    int *arr2d; 
} Foobar_t; 


int main(int argc, char** argv) { 
    Foobar_t foobar = {(int*)testArr}; 
    int (*arr2d)[3] = (int (*)[3]) foobar.arr2d; 
    printf("testStruct_0_0: %d\n", arr2d[0][0]); 
    printf("testStruct_1_0: %d\n", arr2d[1][0]); 

    return 1337; 
} 

Edit:

Einige Kommentare deuten darauf hin, dass die Bezugnahme nicht das richtige Wort ist. Natürlich wird dies in der C-Sprache durch einen Zeiger implementiert. Die TLDR dieser Fragen lautet also: Wie sieht die Syntax für einen Zeigertyp zu einem multidimensionalen Array aus?

Die Antwort ist bereits in meinem Work-Around-Code zu sehen. Also das ist alles, fahr mit, nichts zu sehen;) Trotzdem danke für die Antworten.

+0

Es gibt keinen Hinweis in C –

+1

Sie nicht unvollständige Typen in einer Struktur mit Ausnahme der FAM haben, können bei das Ende einer Struktur mit mindestens einem anderen Mitglied (daher Ihr 'Dummy'-Mitglied). Und wenn Sie einen FAM haben, müssen Sie die Struktur dynamisch mit der richtigen Menge an Speicher belegen. –

+0

In Ihrer Problemumgehung könnte Ihre Zeile 'Foobar_t foobar = {(int *) testArr};' sauberer geschrieben werden als 'Foobar_t foobar = {& testArr [0] [0]};' - keine Umwandlung notwendig. Die zweite Besetzung ist nicht so leicht zu vermeiden. –

Antwort

0

nur an der Suche Es stellte sich heraus Behelfslösung zeigte mir die Lösung:

typedef struct { 
    int (*arr2d)[3]; 
} Foobar_t; 

Dies ist der richtige Typ für einen Zeiger auf ein 2D-Array. type (*name)[n] funktioniert auch für Funktionsparameter.

Warum ist die andere Syntax type name[][n] immer noch gültig für Funktionsparameter? Wahrscheinlich verhindern die Konflikte mit der flexiblen Array-Funktion von Strukturen, dass sie dort arbeiten.

0

Sie könnten versuchen, entweder int **arr2d, die Sie über arr2d[x][y] zugreifen könnten oder versuchen, es einfach in ein eindimensionales Array wie int *arr2d = malloc(2*3*sizeof(int)); konvertieren. Auf diese Weise müssen Sie auf Werte wie folgt zugreifen: arr2d[x*m + y];, wobei x und y die gleichen wie im vorherigen Beispiel sind, während m die Größe einer Zeile ist.

Ich würde Ihnen auch vorschlagen, sowohl die Zeilennummer als auch die Spaltennummer Ihres 2-dimensionalen Arrays in der Struktur zu speichern.

+1

Wenn der Typ "int **" ist und über 'arr2d [x] [y]' aufgerufen wird, wie weiß der Compiler dann, dass y einen Offset von 1 int und x einen Offset von 3 ints hat? – cubei

+0

Es weiß einfach nichts davon; Tatsächlich sollten Sie zusätzliche Argumente übergeben, um "manuell" zu überprüfen, dass Sie keine Größe überschreiten. Denken Sie daran, obwohl Sie int ** Variablen verwenden sollten, sollten Sie jeden einzelnen wie folgt manuell initialisieren: 'int ** arr2d;' '// n = Anzahl der Zeilen, m = Anzahl der Spalten -> allocate n * m "places" ' ' arr2d = (int **) malloc (n * m * sizeof (int)); ' ' // für jede Zeile i, weise m "places" ' ' für (int i = 0; i

+0

Wenn es nicht bekannt ist, dann ein Zugriff über' arr2d [x] [y] 'ist ohne Casting nicht möglich, wie oben beschrieben. Das Malloc-Zeug bezieht sich nicht auf die Questen. Weil es gesagt wurde, dass das Array bereits verfügbar ist und nur bezeichnet werden sollte. – cubei

1

Es gibt keinen "call by reference" in der Sprache C. Funktionsargumente sind immer nach Wert übergeben. Arrays erscheinen besonders, da ein Array in den meisten Ausdrücken (einschließlich Funktionsaufrufen) zu einem Zeiger auf sein erstes Element zerfällt. Das bedeutet, wenn ein Array als Argument in einem Funktionsaufruf verwendet wird, wird anstelle eines Arrays ein Zeiger auf das erste Element an die Funktion übergeben. aber es ist der Wert dieses Zeigers, der übergeben wird.

In Funktionsdeklaratoren werden Array-Typen an Zeiger auf geeignete Typen angepasst. Dies ist spezifisch für die Semantik von Funktionsdeklaratoren. Somit wird eine Funktionsdeklaration wie:

void foobar(int arg[][3]); 

eingestellt wird, um einen Zeiger auf ein Feld von drei int s als Argument zu nehmen:

void foobar(int (*arg)[3]); 

Im Allgemeinen ist ein Ausdruck des Typs, wie int arg[][3] ist ein unvollständiger Typ, da es unmöglich ist, die Größe des Arrays arg[][] ohne weitere Informationen zu kennen.

Strukturen in C erlauben nicht die Angabe von Elementtypen mit unvollständigen Typen (mit einer Ausnahme), da es keine Möglichkeit gibt, die Größe des struct ohne diese Informationen zu kennen. Darüber hinaus nehmen struct Spezifizierer nicht die gleiche Anpassung an Array-Typen vor, die Funktionsprogrammierer ausführen, da struct s tatsächlich Array-Member enthalten können.

Die Ausnahme von der Regel für den unvollständigen Typ in struct ist mit flexible Array-Mitglieder. Das letzte Mitglied einer struct mit mindestens zwei benannten Mitgliedern möglicherweise einen unvollständigen Array-Typ.

Die einfache Lösung für das Problem in der Frage ist, den Bezeichner für die struct zu ändern, einen Zeiger auf ein Array zu verwenden. Beachten Sie, dass hier das Element .arr2d kein Array, sondern ein Zeiger auf ein Array von drei int s:

typedef struct { 
    int (*arr2d)[3]; 
} Foobar_t; 
+0

Call-by-Reference bedeutet, dass Daten durch eine Referenz angegeben werden: hier die Adresse, die durch den Operator * dereferenziert werden kann (oder implizit mit dem Operator -> für einige Typen). – cubei

+0

Danke für die Antwort. 'int (* arr2d) [3]' war was ich suchte. Ich bemerkte es in dem Moment, als ich die Frage posten, weil ich in meiner Workaround genau diesen Typ verwendete ... – cubei

+0

@ cuei- ich sah, dass du die Lösung gefunden hast, aber nicht den Grund, dass 'int arr2d [] [3] 'würde in Funktionsdeklaratoren funktionieren, aber nicht in' struct'-Spezifizierern; Das war der Hauptstoß meiner Antwort. Außerdem hat C keine Referenzen, sondern Zeiger. Einige Sprachen verwenden Referenzen, aber in C ist es immer ein Wert, der an eine Funktion übergeben wird (zum Beispiel ein "int" oder ein Zeiger auf ein "int"); die Unterscheidung ist wichtig. –