78 votes

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

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

A titre d'exemple :

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

155voto

En C, les tableaux 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 c'est changer le tableau lui-même (nombre d'éléments...) vous ne pouvez pas le faire avec des tableaux de pile ou globaux, seulement avec de la mémoire allouée dynamiquement dans le tas. Dans ce cas, si vous voulez changer le pointeur, vous devez passer un pointeur vers celui-ci :

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 l'édition de la question, vous demandez spécifiquement de passer un tableau de structs. Vous avez deux solutions : déclarer un typedef, ou rendre explicite que vous passez un 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 typedéfinir le type comme vous le déclarez (et c'est un idiome courant en C) :

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

12voto

John Bode Points 33046

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

En C, lorsqu'un identificateur de tableau apparaît dans un contexte autre que celui d'une opérande de & ou sizeof, le type de l'identificateur est implicitement converti de "tableau à N éléments de T" en "pointeur vers T", et sa valeur est implicitement fixée à l'adresse du premier élément du tableau (qui est la même que l'adresse du tableau lui-même). C'est pourquoi, lorsque vous passez simplement l'identifiant du tableau comme argument à une fonction, la fonction reçoit un pointeur vers le type de base, plutôt qu'un tableau. Puisqu'il est impossible de connaître la taille d'un tableau en regardant simplement le pointeur du premier élément, vous devez passer la taille comme un 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 autres façons de transmettre des tableaux aux fonctions.

Il existe un pointeur sur un tableau de T, par opposition à un pointeur sur T. Vous déclareriez un tel pointeur sous la forme suivante

T (*p)[N];

Dans ce cas, p est un pointeur vers un tableau à N éléments de T (par opposition à T *p[N], où p est un tableau à N éléments de pointeur vers T). Vous pouvez donc passer un pointeur sur le tableau plutôt qu'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, puisqu'un pointeur vers un tableau de 10 éléments de T est d'un type différent de celui d'un pointeur vers un tableau de 20 éléments de T.

Une troisième méthode consiste à envelopper le tableau dans un struct :

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'avez pas à vous occuper 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).

9voto

JaredPar Points 333733

Le langage C ne prend pas en charge le passage par référence, quel que soit le type. L'équivalent le plus proche est de passer un pointeur vers le type.

Voici un exemple artificiel dans les deux langues

API de style C++

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

L'équivalent C le plus proche

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

8voto

user128026 Points 503

Sachez également que si vous créez un tableau dans une méthode, vous ne pouvez pas le retourner. Si vous renvoyez un pointeur sur celui-ci, il aura été retiré de la pile au retour de la fonction. Vous devez allouer de la mémoire sur le tas et renvoyer un pointeur vers celle-ci. 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)
}

6voto

haffax Points 2837

En C, 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 ce qui se rapproche le plus de l'appel par référence en C.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X