Übergeben eines Arrays als Referenz in C?

Lesezeit: 8 Minuten

Benutzeravatar von Hannoun Yassir
Hannum Jassir

Wie kann ich ein Array von Strukturen als Referenz in C übergeben?

Als Beispiel:

struct Coordinate {
   int X;
   int Y;
};
SomeMethod(Coordinate *Coordinates[]){
   //Do Something with the array
}
int main(){ 
   Coordinate Coordinates[10];
   SomeMethod(&Coordinates);
}

David Rodríguez - Benutzeravatar von dribeas
David Rodríguez – Dribeas

In C werden Arrays als Zeiger auf das erste Element übergeben. Sie sind das einzige Element, das nicht wirklich als Wert übergeben wird (der Zeiger wird als Wert übergeben, aber das Array wird nicht kopiert). Dadurch kann die aufgerufene Funktion den Inhalt ändern.

void reset( int *array, int size) {
   memset(array,0,size * sizeof(*array));
}
int main()
{
   int array[10];
   reset( array, 10 ); // sets all elements to 0
}

Wenn Sie nun das Array selbst ändern möchten (Anzahl der Elemente …), können Sie dies nicht mit Stapel- oder globalen Arrays tun, sondern nur mit dynamisch zugewiesenem Speicher im Heap. Wenn Sie in diesem Fall den Zeiger ändern möchten, müssen Sie einen Zeiger darauf übergeben:

void resize( int **p, int size ) {
   free( *p );
   *p = malloc( size * sizeof(int) );
}
int main() {
   int *p = malloc( 10 * sizeof(int) );
   resize( &p, 20 );
}

In der Fragebearbeitung fragen Sie speziell nach dem Übergeben eines Arrays von Strukturen. Sie haben dort zwei Lösungen: Deklarieren Sie eine Typedef oder machen Sie explizit, dass Sie eine Struktur übergeben:

struct Coordinate {
   int x;
   int y;
};
void f( struct Coordinate coordinates[], int size );
typedef struct Coordinate Coordinate;  // generate a type alias 'Coordinate' that is equivalent to struct Coordinate
void g( Coordinate coordinates[], int size ); // uses typedef'ed Coordinate

Sie können den Typ so definieren, wie Sie ihn deklarieren (und es ist eine gängige Redewendung in C):

typedef struct Coordinate {
   int x;
   int y;
} Coordinate;

  • Verwenden Sie memset (Array, 0, Größe des Arrays); statt for() Zyklus innerhalb des Resets 🙂

    – M4rk

    12. Dezember 2012 um 17:28 Uhr

  • @rodi: Das ist ein interessanter Punkt, wenn man bedenkt, dass Sie einen Fehler eingeführt haben 🙂 Ich habe den zu verwendenden Code aktualisiert memset wie es verwendet werden soll: memset(array,0,size * sizeof *array)sizeof(array) in diesem Fall ist die Größe von a Zeigernicht die punktierten Daten.

    – David Rodríguez – Dribeas

    12. Dezember 2012 um 18:45 Uhr

  • Es ist eine gute Übung nicht zu type cast den Rückgabewert von malloc. siehe hier

    – Aka

    25. Februar 2018 um 0:40 Uhr

  • @Aka: Du hast absolut Recht, und das habe ich gelernt, seit ich das vor etwa 9 Jahren geschrieben habe 🙂 [I have edited to remove the casts, thanks for bringing this up]

    – David Rodríguez – Dribeas

    3. März 2018 um 21:19 Uhr

  • Warum sizeof(*array), wenn der Array-Typ auf int beschränkt ist? Warum nicht einfach sizeof(int)?

    – Elias Hasle

    2. März 2019 um 5:33 Uhr

Um einige der Antworten hier ein wenig zu erweitern …

Wenn in C ein Array-Bezeichner in einem anderen Kontext als als Operand für & oder sizeof erscheint, wird der Typ des Bezeichners implizit von “N-Element-Array von T” in “Zeiger auf T” konvertiert, und sein Wert ist implizit auf die Adresse des ersten Elements im Array gesetzt (das mit der Adresse des Arrays selbst identisch ist). Wenn Sie den Array-Bezeichner einfach als Argument an eine Funktion übergeben, erhält die Funktion daher einen Zeiger auf den Basistyp und kein Array. Da Sie die Größe eines Arrays nicht nur anhand des Zeigers auf das erste Element erkennen können, müssen Sie die Größe als separaten Parameter übergeben.

struct Coordinate { int x; int y; };
void SomeMethod(struct Coordinate *coordinates, size_t numCoordinates)
{
    ...
    coordinates[i].x = ...;
    coordinates[i].y = ...; 
    ...
}
int main (void)
{
    struct Coordinate coordinates[10];
    ...
    SomeMethod (coordinates, sizeof coordinates / sizeof *coordinates);
    ...
}

Es gibt ein paar alternative Möglichkeiten, Arrays an Funktionen zu übergeben.

Es gibt so etwas wie einen Zeiger auf ein Array von T, im Gegensatz zu einem Zeiger auf T. Sie würden einen solchen Zeiger als deklarieren

T (*p)[N];

In diesem Fall ist p ein Zeiger auf ein N-elementiges Array von T (im Gegensatz zu T *p[N], wobei p ein N-elementiges Array eines Zeigers auf T ist). Sie könnten also einen Zeiger auf das Array übergeben, im Gegensatz zu einem Zeiger auf das erste Element:

struct Coordinate { int x; int y };

void SomeMethod(struct Coordinate (*coordinates)[10])
{
    ...
    (*coordinates)[i].x = ...;
    (*coordinates)[i].y = ...;
    ...
}

int main(void)
{
    struct Coordinate coordinates[10];
    ...
    SomeMethod(&coordinates);
    ...
}

Der Nachteil dieser Methode besteht darin, dass die Array-Größe festgelegt ist, da ein Zeiger auf ein 10-Elemente-Array von T einen anderen Typ hat als ein Zeiger auf ein 20-Elemente-Array von T.

Eine dritte Methode besteht darin, das Array in eine Struktur einzuschließen:

struct Coordinate { int x; int y; };
struct CoordinateWrapper { struct Coordinate coordinates[10]; };
void SomeMethod(struct CoordinateWrapper wrapper)
{
    ...
    wrapper.coordinates[i].x = ...;
    wrapper.coordinates[i].y = ...;
    ...
}
int main(void)
{
    struct CoordinateWrapper wrapper;
    ...
    SomeMethod(wrapper);
    ...
}

Der Vorteil dieser Methode ist, dass Sie nicht mit Zeigern herumspielen müssen. Der Nachteil ist, dass die Array-Größe festgelegt ist (auch hier ist ein 10-Elemente-Array von T ein anderer Typ als ein 20-Elemente-Array von T).

  • Ein weiterer Nachteil Ihrer Wrapper-Formulierung ist das wrapper vorbeigefahren wird Wert. 80 Bytes (-ish) und die Zuweisung darin SomeMethod() hat keinen Einfluss auf die wrapper deklariert in main. Sieht aus wie ein Fehler.

    – Bobbogo

    16. Dezember 2015 um 14:10 Uhr

Die C-Sprache unterstützt keine Referenzübergabe jeglicher Art. Das nächste Äquivalent besteht darin, einen Zeiger auf den Typ zu übergeben.

Hier ist ein erfundenes Beispiel in beiden Sprachen

API im C++-Stil

void UpdateValue(int& i) {
  i = 42;
}

Nächstes C-Äquivalent

void UpdateValue(int *i) {
  *i = 42;
}

  • Ich weiß, aber ich finde das Problem, ein Array mit derselben Methode zu übergeben: s

    – Hannun Jassir

    9. Juli 2009 um 23:31 Uhr

  • @Yassir, kannst du ein Beispiel geben?

    – JaredPar

    9. Juli 2009 um 23:32 Uhr

  • Dieses Beispiel demonstriert die Übergabe eines primitiven Typs per Referenz (unter Verwendung eines Zeigers, der als Wert übergeben wird), aber nicht die Übergabe eines Arrays von Strukturen per Referenz, wie es im folgenden Beitrag richtig gezeigt wird.

    – Ralf Caraveo

    9. Juli 2009 um 23:40 Uhr

  • Ich meine, in Dribeas Post richtig demonstriert! =)

    – Ralf Caraveo

    9. Juli 2009 um 23:44 Uhr

  • @Ralph, und du glaubst, es war eine -1 wert? Das OP hat eine Frage gestellt, die keine gültige Antwort hat, und ich habe eine einfache Antwort “nächste Funktion ist …” gegeben. Vielleicht keine +1-Antwort, aber sicherlich keine -1

    – JaredPar

    9. Juli 2009 um 23:46 Uhr

Beachten Sie auch, dass Sie ein Array, das Sie innerhalb einer Methode erstellen, nicht zurückgeben können. Wenn Sie einen Zeiger darauf zurückgeben, wäre er vom Stack entfernt worden, wenn die Funktion zurückkehrt. Sie müssen dem Heap Speicher zuweisen und einen Zeiger darauf zurückgeben. z.B.

//this is bad
char* getname()
{
  char name[100];
  return name;
}

//this is better
char* getname()
{
  char *name = malloc(100);
  return name;
  //remember to free(name)
}

Arrays werden standardmäßig per Referenz übergeben. Tatsächlich wird der Wert des Zeigers auf das erste Element übergeben. Daher kann die Funktion oder Methode, die dies empfängt, die Werte im Array ändern.

void SomeMethod(Coordinate Coordinates[]){Coordinates[0].x++;};
int main(){
  Coordinate tenCoordinates[10];
  tenCoordinates[0].x=0;
  SomeMethod(tenCoordinates[]);
  SomeMethod(&tenCoordinates[0]);
  if(0==tenCoordinates[0].x - 2;){
    exit(0);
  }
  exit(-1);
}

Die beiden Aufrufe sind äquivalent und der Exit-Wert sollte 0 sein;

Benutzeravatar von haffax
Haffax

In einfachem C können Sie eine Kombination aus Zeiger und Größe in Ihrer API verwenden.

void doSomething(MyStruct* mystruct, size_t numElements)
{
    for (size_t i = 0; i < numElements; ++i)
    {
        MyStruct current = mystruct[i];
        handleElement(current);
    }
}

Die Verwendung von Zeigern kommt dem in C verfügbaren Call-by-Reference am nächsten.

Benutzeravatar von Jeff M
Jeff M

Hey Leute, hier ist ein einfaches Testprogramm, das zeigt, wie ein Array mit new oder malloc zugewiesen und übergeben wird. Einfach ausschneiden, einfügen und ausführen. Habe Spaß!

struct Coordinate
{
    int x,y;
};

void resize( int **p, int size )
{
   free( *p );
   *p = (int*) malloc( size * sizeof(int) );
}

void resizeCoord( struct Coordinate **p, int size )
{
   free( *p );
   *p = (Coordinate*) malloc( size * sizeof(Coordinate) );
}

void resizeCoordWithNew( struct Coordinate **p, int size )
{
   delete [] *p;
   *p = (struct Coordinate*) new struct Coordinate[size];
}

void SomeMethod(Coordinate Coordinates[])
{
    Coordinates[0].x++;
    Coordinates[0].y = 6;
}

void SomeOtherMethod(Coordinate Coordinates[], int size)
{
    for (int i=0; i<size; i++)
    {
        Coordinates[i].x = i;
        Coordinates[i].y = i*2;
    }
}

int main()
{
    //static array
    Coordinate tenCoordinates[10];
    tenCoordinates[0].x=0;
    SomeMethod(tenCoordinates);
    SomeMethod(&(tenCoordinates[0]));
    if(tenCoordinates[0].x - 2  == 0)
    {
        printf("test1 coord change successful\n");
    }
    else
    {
        printf("test1 coord change unsuccessful\n");
    }


   //dynamic int
   int *p = (int*) malloc( 10 * sizeof(int) );
   resize( &p, 20 );

   //dynamic struct with malloc
   int myresize = 20;
   int initSize = 10;
   struct Coordinate *pcoord = (struct Coordinate*) malloc (initSize * sizeof(struct Coordinate));
   resizeCoord(&pcoord, myresize); 
   SomeOtherMethod(pcoord, myresize);
   bool pass = true;
   for (int i=0; i<myresize; i++)
   {
       if (! ((pcoord[i].x == i) && (pcoord[i].y == i*2)))
       {        
           printf("Error dynamic Coord struct [%d] failed with (%d,%d)\n",i,pcoord[i].x,pcoord[i].y);
           pass = false;
       }
   }
   if (pass)
   {
       printf("test2 coords for dynamic struct allocated with malloc worked correctly\n");
   }


   //dynamic struct with new
   myresize = 20;
   initSize = 10;
   struct Coordinate *pcoord2 = (struct Coordinate*) new struct Coordinate[initSize];
   resizeCoordWithNew(&pcoord2, myresize); 
   SomeOtherMethod(pcoord2, myresize);
   pass = true;
   for (int i=0; i<myresize; i++)
   {
       if (! ((pcoord2[i].x == i) && (pcoord2[i].y == i*2)))
       {        
           printf("Error dynamic Coord struct [%d] failed with (%d,%d)\n",i,pcoord2[i].x,pcoord2[i].y);
           pass = false;
       }
   }
   if (pass)
   {
       printf("test3 coords for dynamic struct with new worked correctly\n");
   }


   return 0;
}

1417430cookie-checkÜbergeben eines Arrays als Referenz in C?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy