Een array doorgeven via referentie in C?

Hoe kan ik een array van structs doorgeven als referentie in C?

Als voorbeeld:

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

Antwoord 1, autoriteit 100%

In C worden arrays als een pointer naar het eerste element doorgegeven. Ze zijn het enige element dat niet echt wordt doorgegeven door waarde (de aanwijzer wordt doorgegeven door waarde, maar de array wordt niet gekopieerd). Hierdoor kan de aangeroepen functie de inhoud wijzigen.

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
}

Als je nu de array zelf wilt veranderen (aantal elementen…), kun je dat niet doen met stack- of globale arrays, alleen met dynamisch toegewezen geheugen in de heap. In dat geval, als u de aanwijzer wilt wijzigen, moet u er een aanwijzer aan doorgeven:

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 de vraagbewerking stel je specifiek vragen over het doorgeven van een reeks structs. Je hebt daar twee oplossingen: declareer een typedef, of maak expliciet dat je een struct doorgeeft:

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

Je kunt het type typen zoals je het aangeeft (en het is een veelgebruikt idioom in C):

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

Antwoord 2, autoriteit 8%

Om een beetje uit te weiden over enkele van de antwoorden hier…

In C, wanneer een array-ID verschijnt in een andere context dan als een operand voor & of sizeof, het type identifier wordt impliciet geconverteerd van “N-element array van T” naar “pointer to T”, en de waarde wordt impliciet ingesteld op het adres van het eerste element in de array (wat hetzelfde is als de adres van de array zelf). Dat is de reden waarom wanneer u de array-ID als argument aan een functie doorgeeft, de functie een verwijzing naar het basistype ontvangt in plaats van een array. Omdat je niet kunt zien hoe groot een array is door alleen maar naar de aanwijzer naar het eerste element te kijken, moet je de grootte als een afzonderlijke parameter doorgeven.

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);
    ...
}

Er zijn een aantal alternatieve manieren om arrays door te geven aan functies.

Er bestaat zoiets als een aanwijzer naar een array van T, in tegenstelling tot een aanwijzer naar T. Je zou zo’n aanwijzer declareren als

T (*p)[N];

In dit geval is p een pointer naar een N-element array van T (in tegenstelling tot T *p[N], waar p een N-element array van pointer naar T is). U kunt dus een aanwijzer naar de array doorgeven in plaats van een aanwijzer naar het eerste 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);
    ...
}

Het nadeel van deze methode is dat de arraygrootte vast is, aangezien een pointer naar een array van 10 elementen van T een ander type is dan een pointer naar een array van 20 elementen van T.

Een derde methode is om de array in een struct te wikkelen:

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);
    ...
}

Het voordeel van deze methode is dat je niet aan het rommelen bent met pointers. Het nadeel is dat de arraygrootte vast is (nogmaals, een array met 10 elementen van T is een ander type dan een array met 20 elementen van T).


Antwoord 3, autoriteit 6%

De C-taal ondersteunt geen enkele type pass-by-referentie. Het dichtstbijzijnde equivalent is om een aanwijzer door te geven aan het type.

Hier is een gekunsteld voorbeeld in beide talen

C++-stijl API

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

Dichtstbijzijnde C-equivalent

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

Antwoord 4, autoriteit 6%

Houd er ook rekening mee dat als u een array binnen een methode maakt, u deze niet kunt retourneren. Als u er een aanwijzer naar terugstuurt, zou deze van de stapel zijn verwijderd wanneer de functie terugkeert.
je moet geheugen toewijzen aan de heap en daar een pointer naar terugsturen.
bijv.

//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)
}

Antwoord 5, autoriteit 4%

In gewone C kun je een aanwijzer/grootte-combinatie gebruiken in je API.

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

Het gebruik van pointers komt het dichtst in de buurt van call-by-reference beschikbaar in C.


Antwoord 6, autoriteit 4%

Arrays worden standaard effectief doorgegeven via referentie. Eigenlijk wordt de waarde van de aanwijzer naar het eerste element doorgegeven. Daarom kan de functie of methode die dit ontvangt de waarden in de array wijzigen.

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);
}

De twee oproepen zijn equivalent en de exit-waarde moet 0 zijn;


Antwoord 7

Hallo, hier is een eenvoudig testprogramma dat laat zien hoe je een array kunt toewijzen en doorgeven met new of malloc. Gewoon knippen, plakken en uitvoeren. Veel plezier!

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;
}

Other episodes