46 votes

Déclarer une fonction C pour retourner un tableau

Comment puis-je créer une fonction qui retourne un tableau ? J'ai essayé ceci

const int WIDTH=11;
const int HEIGHT=11;

int main() {
  char A[WIDTH][HEIGHT];
  A=rand_grid(WIDTH,HEIGHT);
  return 0;
}

// Initialise un tableau aléatoire.
char[][] rand_grid(int i, int k) {
  char* A[i][k];
  for(j=0;j

78voto

John Bode Points 33046

Plusieurs choses à souligner.

Tout d'abord, vous ne pouvez pas assigner un objet tableau comme vous le faites ici :

char A[LARGEUR][HAUTEUR];  
A=rand_grid(LARGEUR,HAUTEUR);

Les objets de type tableau ne sont pas modifiables.

Deuxièmement, les fonctions en C ne peuvent pas retourner des types de tableau. Ils peuvent cependant retourner des pointeurs vers des tableaux :

char (*foo(int largeur))[HAUTEUR]
{
  /**
   * allouer dynamiquement la mémoire pour un tableau largeur x HAUTEUR de char
   */
  char (*newArr)[HAUTEUR] = malloc(sizeof *newArr * largeur);
  /**
   * initialiser le contenu du tableau ici
   */
  return newArr;
}

La syntaxe est un peu déroutante; cela se lit comme

       foo                                   -- foo
       foo(int largeur)                       -- est une fonction
                                             -- prenant un paramètre entier
      *foo(int largeur)                       -- retournant un pointeur
     (*foo(int largeur))[HAUTEUR]             -- vers un tableau de HAUTEUR éléments
char (*foo(int largeur))[HAUTEUR]             -- de char

Pour C89, HAUTEUR dans le extrait ci-dessus doit être une expression intégrale constante au moment de la compilation (soit une macro, un littéral numérique, ou une expression arithmétique consistant en des macros et/ou des littéraux numériques). Je ne suis pas sûr si c'est aussi vrai pour C99.

Basé sur le extrait que vous avez posté, ce que vous voulez faire est de prendre un tableau que vous avez déjà alloué et d'initialiser ses contenus. N'oubliez pas que dans la plupart des contextes, une expression d'un type de tableau sera implicitement convertie en un pointeur vers le type de base. Autrement dit, si vous passez un tableau de N éléments de T à une fonction, ce que la fonction reçoit réellement est un pointeur vers T :

void foo (T *p) {...}
...
T arr[N];
foo(arr);

Pour les tableaux à 2 dimensions, c'est un peu plus compliqué :

void foo(T (*p)[M]) {...}
...
T arr[N][M];
foo(arr);

Cela repose également sur le fait que M soit connu au moment de la compilation, ce qui limite l'utilité de la fonction. Ce que vous aimeriez, c'est une fonction qui peut traiter un tableau à 2 dimensions de taille arbitraire. La meilleure façon que je connaisse pour y parvenir est au lieu de passer un pointeur vers le tableau, de passer l'adresse du premier élément dans le tableau[1], et de passer le nombre de lignes et de colonnes en tant que paramètres séparés :

void foo(T *base, size_t lignes, size_t colonnes) {...}
...
T arr[N][M];
foo (&arr[0][0], N, M);

Ainsi, votre fonction rand_grid ressemblerait à ceci :

void rand_grid(char *base, size_t lignes, size_t colonnes)
{
  size_t i, j;
  for (i = 0; i < lignes; i++)
  {
    for (j = 0; j < colonnes; j++)
    {
      /**
       * Comme base est un simple char *, nous devons l'indexer
       * comme s'il pointait vers un tableau 1D. Cela fonctionne si
       * base pointe vers le premier élément d'un tableau 2D,
       * car les tableaux multidimensionnels sont contigus.  
       */
      base[i*colonnes+j] = initial_value();
    }
  }
}

int main(void)
{
  char A[LARGEUR][HAUTEUR];
  rand_grid(&A[0][0], LARGEUR, HAUTEUR);
  ...
}

  1. Même si les expressions &A[0][0] et A donnent la même valeur (l'adresse de base de A), les types des deux expressions sont différents. La première expression est évaluée comme un simple pointeur de char (char *), tandis que la seconde est évaluée comme un pointeur vers un tableau 2D de char (char (*)[HAUTEUR]).

15voto

Vous ne pouvez pas. Vous pouvez soit passer un pointeur vers un tableau en tant que paramètre et faire en sorte que la fonction le modifie, soit la fonction elle-même peut allouer des données et renvoyer un pointeur.

dans votre cas

void rand_grid(char A[LARGEUR][HAUTEUR]) {
    A[0][0] = 'A'; // ou ce que vous avez l'intention de faire
}

main() {
    char A[LARGEUR][HAUTEUR];
    rand_grid(A);
}

Éditer : Comme caf l'a souligné, on peut en fait renvoyer la struct avec un tableau dedans, mais bien sûr aucun programmeur en C sain d'esprit ne ferait cela.

11voto

unwind Points 181987

Vous ne pouvez jamais retourner une variable allouée sur la pile ("auto") d'autre chose qu'un type primitif (valeur), et des struct de ce type. Pour d'autres types, vous devez allouer la mémoire à partir du tas, en utilisant malloc(), ou envelopper le tableau de taille fixe dans une struct.

Si vous utilisez un tableau de taille fixe, vous pouvez le modéliser comme une struct et utiliser le retour de la struct :

#define WIDTH  11
#define HEIGHT 11

typedef struct {
  unsigned char cell[WIDTH * HEIGHT];
} Board;

Board board_new(void)
{
  Board b;
  size_t i;

  for(i = 0; i < sizeof b.cell / sizeof *b.cell; i++)
    b.cell[i] = rand() & 255;
  return b;
}

C'est bien, et cela ne devrait pas être plus coûteux que l'alternative, en utilisant un pointeur explicite :

void board_init(Board *b);

Puisque le premier cas de retour de struct peut être réécrit (par le compilateur) en le dernier. Cela s'appelle l'optimisation des valeurs de retour.

1voto

sud03r Points 6093

Si vous voulez vraiment le faire, vous pouvez essayer de rendre le tableau A statique, de cette façon le stockage pour A n'est pas déterminé par la portée de la fonction et vous pouvez en fait retourner le tableau (sous forme de pointeur bien sûr).

Mais ce n'est pas une bonne manière d'accomplir ce que vous essayez d'atteindre, passez plutôt le tableau à la fonction rand_grid. C'est à cela que sert le passage par adresse.

0voto

user324903 Points 1

Toutes les méthodes que je connais pour retourner un tableau à partir d'une fonction ont des faiblesses et des forces.

L'enrobage dans une structure évite les coûts supplémentaires de l'allocation et de la libération de mémoire, ainsi que le problème de devoir se souvenir de libérer la mémoire. Vous avez ces problèmes avec toute solution utilisant malloc, calloc et realloc. D'autre part, l'enrobage dans une structure nécessite de connaître la taille maximale possible du tableau, et est clairement gaspilleur de mémoire et de temps d'exécution pour les grands tableaux (par exemple, charger un fichier en mémoire et passer le contenu du fichier de fonction en fonction en le copiant).

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