Retour de tableaux multidimensionnels à partir d’une fonction en C

Quel est le meilleur moyen de retourner un tableau multidimensionnel à partir d’une fonction dans c?

Supposons que nous devions générer un tableau multidimensionnel dans une fonction et l’appeler en tant que main. Est-il préférable de l’envelopper dans une structure ou de simplement renvoyer un pointeur en mémoire sur le tas?

int *create_array(int rows, int columns){ int array[rows][columns] = {0}; return array; } int main(){ int row = 10; int columns = 2; create_array(row,columns); } 

Le code ci-dessus est juste pour esquisser le programme de base que j’ai en tête.

C’est faux:

 int *create_array(int rows, int columns){ int array[rows][columns] = {0}; return array; } 

et devrait produire un avertissement comme celui-ci:

 prog.c:2:6: note: (near initialization for 'array') prog.c:3:13: warning: return from incompatible pointer type [-Wincompatible-pointer-types] return array; ^~~~~ prog.c:3:13: warning: function returns address of local variable [-Wreturn-local-addr] 

puisque vous retournez l’ adresse d’une variable automatique; sa durée de vie se termine lorsque sa fonction correspondante se termine.


Vous devez soit déclarer un double pointeur dans main() , le passer à travers la fonction, allouer dynamicment de la mémoire à celui-ci et renvoyer ce pointeur. Ou vous pouvez créer le tableau dans main() et passer le double pointeur à la fonction.


Je veux savoir comment allouer des tableaux multidimensionnels sur le tas et les faire circuler

Pour allouer de la mémoire sur le tas, vous pouvez utiliser l’une de ces deux méthodes, qui impliquent des pointeurs :

 #include  #include  // We return the pointer int **get(int N, int M) /* Allocate the array */ { /* Check if allocation succeeded. (check for NULL pointer) */ int i, **array; array = malloc(N*sizeof(int *)); for(i = 0 ; i < N ; i++) array[i] = malloc( M*sizeof(int) ); return array; } // We don't return the pointer void getNoReturn(int*** array, int N, int M) { /* Check if allocation succeeded. (check for NULL pointer) */ int i; *array = malloc(N*sizeof(int *)); for(i = 0 ; i < N ; i++) (*array)[i] = malloc( M*sizeof(int) ); } void fill(int** p, int N, int M) { int i, j; for(i = 0 ; i < N ; i++) for(j = 0 ; j < M ; j++) p[i][j] = j; } void print(int** p, int N, int M) { int i, j; for(i = 0 ; i < N ; i++) for(j = 0 ; j < M ; j++) printf("array[%d][%d] = %d\n", i, j, p[i][j]); } void freeArray(int** p, int N) { int i; for(i = 0 ; i < N ; i++) free(p[i]); free(p); } int main(void) { int **p; //getNoReturn(&p, 2, 5); p = get(2, 5); fill(p ,2, 5); print(p, 2, 5); freeArray(p ,2); return 0; } 

Choisissez celui qui convient le mieux à votre style.

Quel est le meilleur moyen de retourner un tableau multidimensionnel à partir d’une fonction dans c?

Ma recommandation est d’éviter de faire cela, et d’éviter les tableaux multidimensionnels en C (ils sont illisibles et gênants).

Je recommanderais de faire de votre type de masortingce votre type de données abstrait approprié, représenté par une struct terminant par un membre de tableau flexible :

 struct mymasortingx_st { unsigned nbrows, nbcolumns; int values[]; }; 

Voici la fonction de création (renvoyant un pointeur correctement initialisé à la mémoire dynamic ):

 struct mymasortingx_st* create_masortingx(unsigned mnbrows, unsigned mnbcolumns) { if (mnbrows > UINT_MAX/4 || mnbcolumns > UINT_MAX/4 ||(unsigned long)mnbrows * (unsigned long)mnbcolums > UINT_MAX) { fprintf(stderr, "too big masortingx\n"); exit(EXIT_FAILURE); }; size_t sz = sizeof(struct mymasortingx_st)+(mnbrows*mnbcolumns*sizeof(int)); struct mymasortingx_st*m = malloc(sz); if (!m) { perror("malloc mymasortingx"); exit(EXIT_FAILURE); }; m->nbrows = mnbrows; m->nbcolumns = mnbcolumns; for (unsigned long ix=(unsigned long)mnbrows * (unsigned long)mnbcolumns-1; ix>=0; ix--) m->values[ix] = 0; return m;; } /*end create_masortingx*/ 

C’est pour cette raison que struct mymasortingx_st ne contient aucun pointeur intérieur. Vous pouvez et devez utiliser free pour le détruire.

Voici la fonction accesseur; faites-en une fonction static inline et définissez-la dans le même en-tête en déclarant struct mymasortingx_st et create_masortingx , par exemple

 static inline int getmasortingx(struct mymasortingx_st*m, unsigned row, unsigned col) { if (!m) { fprintf(stderr, "getmasortingx with no masortingx\n"); exit(EXIT_FAILURE); }; if (row >= m->nbrows || col >= m->nbcolumns){ fprintf(stderr, "getmasortingx out of bounds\n"); exit(EXIT_FAILURE); }; return m->values[row*m->nbcolumns + col]; } 

Je vous laisse le struct mymasortingx_st de définir et d’implémenter les autres opérations de votre type abstrait struct mymasortingx_st .

(vous pouvez adapter le code, en supprimant peut-être la vérification hors limites, mais je ne recommande pas le code non sécurisé)

 int** create_array(int rows, int columns){ int** array = malloc(rows * sizeof(int*)); int i; for (i=0; i 

devrait faire l'affaire. Si vous utilisez int array[rows][columns]; alors il est mort dès le retour du functiom, et vous obtenez un UB. Vous devriez au moins utiliser l'allocation de mémoire dynamic.

Vous ne pouvez pas renvoyer de tableau, mais vous pouvez également renvoyer un pointeur normal et un document indiquant que l’appelé peut le traiter comme un pointeur sur un tableau multidimensionnel des dimensions qu’il a transmises à l’appelant.

(Notez que le pointeur renvoyé doit pointer sur une mémoire dynamic ou statique, mais pas sur une mémoire automatique – ne renvoyez pas de pointeurs sur des variables locales!)

Il faut des castes légèrement verbeuses et éventuellement une macro mais c’est faisable:

 #include  #include  #include  void* multi(int R, int C) { return calloc ( 1, sizeof(int[R][C]) ); //or sizeof(int)*R*C } int main() { int (*r_)[3][4] = multi(3,4); if(!r_) return EXIT_FAILURE; #define r (*r_) //emulate C++ a reference -- r now behaves as an `int r[3][4];` //Test that addresses advance as they would in a multi-d array int local[3][4]; assert(&local[1][0]-&local[0][0] == 4); //base example assert(&r[1][0]-&r[0][0] == 4); //"returned" multi-d array free(r); //or free(&r) or free(r_) -- here it shouldn't matter #undef r return 0; } 

Notez qu’un tableau de pointeurs n’est pas la même chose qu’un tableau multi-d. Un vrai tableau multi-d est un bloc contigu, alors qu’un tableau de pointeurs (bien qu’il soit utilisable avec la même syntaxe d’indexation) a une localité de référence bien pire, il peut donc être préférable de renvoyer des pointeurs à des pointeurs si vous voulez de meilleures performances.