Passer un tableau par référence en C?

Comment puis-je passer un tableau de structures par référence en C?

À titre d'exemple:

struct Coordinate {
   int X;
   int Y;
};
SomeMethod(Coordinate *Coordinates[]){
   //Do Something with the array
}
int main(){ 
   Coordinate Coordinates[10];
   SomeMethod(&Coordinates);
}
58
demandé sur templatetypedef 2009-07-10 03:27:50

7 réponses

Dans les tableaux C sont passés comme un pointeur sur le premier élément. Ils sont le seul élément qui n'est pas vraiment passé par valeur (le pointeur est passé par valeur, mais le tableau n'est pas copié). Cela permet à la fonction appelée de modifier le contenu.

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
}

Maintenant, si ce que vous voulez est de changer le tableau lui-même (nombre d'éléments...) vous ne pouvez pas le faire avec la pile ou les tableaux globaux, seulement avec la mémoire allouée dynamiquement dans le tas. Dans ce cas, si vous souhaitez modifier le pointeur, vous devez passer un pointeur vers elle:

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

Dans la question edit, vous demandez spécifiquement de passer un tableau de structures. Vous avez deux solutions là-bas: déclarer un typedef, ou rendre explicite que vous passez une struct:

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

Vous pouvez taper le type tel que vous le déclarez (et c'est un idiome commun en C):

typedef struct Coordinate {
   int x;
   int y;
} Coordinate;
118
répondu David Rodríguez - dribeas 2018-03-03 21:18:23

Pour développer un peu sur certaines des réponses ici...

Dans C, lorsqu'un tableau identifiant apparaît dans un contexte autre qu'un opérande soit & ou sizeof, le type de l'identificateur est implicitement converti à partir "N-élément de tableau de T" à "pointeur de T", et sa valeur est implicitement définie à l'adresse du premier élément du tableau (qui est la même que l'adresse de la table elle-même). C'est pourquoi, lorsque vous passez simplement l'identifiant du tableau en tant qu'argument à une fonction, le fonction reçoit un pointeur sur le type de base, plutôt qu'un tableau. Puisque vous ne pouvez pas dire à quel point un tableau est grand simplement en regardant le pointeur vers le premier élément, vous devez passer la taille en tant que paramètre séparé.

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

Il existe deux façons alternatives de passer des tableaux aux fonctions.

Il existe un pointeur vers un tableau de T, par opposition à un pointeur vers T. Vous déclareriez un tel pointeur comme

T (*p)[N];

Dans ce cas, p est un pointeur vers un Tableau n-élément de T (par opposition à T * p[N], où p est un tableau n-élément de pointeur vers T). Vous pouvez donc passer un pointeur sur le tableau par opposition à un pointeur sur le premier élément:

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

L'inconvénient de cette méthode est que la taille du tableau est fixe, depuis un pointeur vers un tableau de 10 éléments de T est un type différent d'un pointeur à un 20-élément de tableau de T.

Une troisième méthode consiste à envelopper le tableau dans une structure:

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

L'avantage de cette méthode est que vous n'êtes pas curage autour avec des pointeurs. L'inconvénient est que la taille du tableau est fixe (encore une fois, un tableau de 10 éléments de T est un type différent d'un tableau de 20 éléments de T).

11
répondu John Bode 2009-07-10 15:23:32

Le langage C ne prend pas en charge le passage par référence de tout type. L'équivalent le plus proche est de passer un pointeur sur le type.

Voici un exemple artificiel dans les deux langues

API de style C++

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

Équivalent C le plus proche

void UpdateValue(int *i) {
  *i = 42;
}
8
répondu JaredPar 2009-07-09 23:29:32

Sachez également que si vous créez un tableau dans une méthode, vous ne pouvez pas le renvoyer. Si vous y renvoyez un pointeur, il aurait été supprimé de la pile lorsque la fonction revient. vous devez allouer de la mémoire sur le tas et renvoyer un pointeur vers cela. par exemple.

//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)
}
7
répondu user128026 2009-07-09 23:42:11

En C simple, vous pouvez utiliser une combinaison pointeur/taille dans votre API.

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

L'utilisation de pointeurs est la plus proche de l'appel par référence disponible dans C.

6
répondu haffax 2009-07-09 23:36:24

Les tableaux

Sont effectivement passés par référence par défaut. En fait, la valeur du pointeur sur le premier élément est passée. Par conséquent, la fonction ou la méthode recevant ceci peut modifier les valeurs dans le tableau.

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

Les deux appels sont équivalents, et la valeur de sortie doit être 0;

6
répondu dlamblin 2009-07-09 23:51:01

Hey les gars voici un programme de test simple qui montre comment allouer et passer un tableau en utilisant new ou malloc. Il suffit de couper, coller et exécuter. Amuse-toi bien!

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;
}
2
répondu Jeff M 2011-12-08 04:21:11