web-dev-qa-db-fra.com

Comment travailler avec des tableaux multidimensionnels dynamiques en C?

Est-ce que quelqu'un sait comment utiliser des tableaux multidimensionnels alloués dynamiquement à l'aide de C? Est-ce possible?

57
rpf

Avec allocation dynamique, en utilisant malloc:

int** x;

x = malloc(dimension1_max * sizeof(int*));
for (int i = 0; i < dimension1_max; i++) {
  x[i] = malloc(dimension2_max * sizeof(int));
}

[...]

for (int i = 0; i < dimension1_max; i++) {
  free(x[i]);
}
free(x);

Cela alloue un tableau 2D de taille dimension1_max * dimension2_max. Ainsi, par exemple, si vous souhaitez un tableau 640 * 480 (par exemple, une image), utilisez dimension1_max = 640, dimension2_max = 480. Vous pouvez ensuite accéder au tableau à l'aide de x[d1][d2]d1 = 0..639, d2 = 0. .479.

Mais une recherche sur SO ou Google révèle également d'autres possibilités, par exemple dans cette question SO

Notez que votre tableau n'allouera pas de région de mémoire contiguë (640 * 480 octets), ce qui pourrait poser des problèmes avec les fonctions qui le supposent. Donc, pour que le tableau satisfasse à la condition, remplacez le bloc malloc ci-dessus par ceci:

int** x;
int* temp;

x = malloc(dimension1_max * sizeof(int*));
temp = malloc(dimension1_max * dimension2_max * sizeof(int));
for (int i = 0; i < dimension1_max; i++) {
  x[i] = temp + (i * dimension2_max);
}

[...]

free(temp);
free(x);
75
schnaader

Depuis C99, 13 ans maintenant, C dispose de tableaux 2D à bornes dynamiques. Si vous voulez éviter que de telles bêtes soient allouées sur la pile (ce que vous devriez), vous pouvez les allouer facilement en une fois, comme suit

double (*A)[n] = malloc(sizeof(double[n][n]));

et c'est tout. Vous pouvez ensuite l'utiliser facilement, comme vous êtes utilisé pour des tableaux 2D avec quelque chose comme A[i][j]. Et n'oubliez pas celui-là à la fin

free(A);

Randy Meyers a écrit une série d'articles expliquant des tableaux de longueur variable (VLA) .

72
Jens Gustedt

Les bases

Les tableaux de c sont déclarés et accessibles à l’aide de l’opérateur []. Pour que

int ary1[5];

déclare un tableau de 5 entiers. Les éléments étant numérotés à partir de zéro, ary1[0] est le premier élément et ary1[4] le dernier. Note1: Il n'y a pas d'initialisation par défaut, donc la mémoire occupée par le tableau peut contenir initialement rien . Note2: ary1[5] accède à la mémoire dans un état non défini (qui peut même ne pas vous être accessible), alors ne le faites pas!

Les tableaux multidimensionnels sont implémentés comme un tableau de tableaux (de tableaux (de ...)). Alors

float ary2[3][5];

déclare un tableau de 3 tableaux unidimensionnels de 5 nombres en virgule flottante chacun. Maintenant, ary2[0][0] est le premier élément du premier tableau, ary2[0][4] est le dernier élément du premier tableau et ary2[2][4] est le dernier élément du dernier tableau. La norme '89 exige que ces données soient contiguës (sec. A8.6.2 à la page 216 de mon K & R 2e éd.) Mais semble être agnostique en ce qui concerne le remplissage.

Essayer d'être dynamique dans plus d'une dimension

Si vous ne connaissez pas la taille du tableau au moment de la compilation, vous souhaiterez allouer le tableau de manière dynamique. Il est tentant d'essayer

double *buf3;
buf3 = malloc(3*5*sizeof(double));
/* error checking goes here */

ce qui devrait fonctionner si le compilateur ne compresse pas l'allocation (collez un espace supplémentaire entre les tableaux unidimensionnels). Il pourrait être plus sûr d'aller avec:

double *buf4;
buf4 = malloc(sizeof(double[3][5]));
/* error checking */

mais de toute façon, le truc vient au moment du déréférencement. Vous ne pouvez pas écrire buf[i][j] car buf a un type incorrect. Tu ne peux pas non plus utiliser

double **hdl4 = (double**)buf;
hdl4[2][3] = 0; /* Wrong! */

parce que le compilateur s'attend à ce que hdl4 soit l'adresse d'une adresse de double. Vous ne pouvez pas non plus utiliser double incomplete_ary4[][]; car il s'agit d'une erreur;

Alors que peux-tu faire?

  • Faire l'arithmétique des lignes et des colonnes vous-même
  • Allouer et faire le travail dans une fonction
  • Utilisez un tableau de pointeurs (le mécanisme dont parle qrdl)

Faites le calcul vous-même

Il suffit de calculer le décalage de mémoire pour chaque élément comme ceci:

  for (i=0; i<3; ++i){
     for(j=0; j<3; ++j){
        buf3[i * 5 + j] = someValue(i,j); /* Don't need to worry about 
                                             padding in this case */
     }
  }

Allouer et faire le travail dans une fonction

Définir une fonction qui prend la taille nécessaire en argument et procéder normalement

void dary(int x, int y){
  double ary4[x][y];
  ary4[2][3] = 5;
}

Bien entendu, dans ce cas, ary4 est une variable locale et vous ne pouvez pas la renvoyer: tout le travail avec le tableau doit être effectué dans la fonction que vous appelez dans les fonctions que it appelle.

Un tableau de pointeurs

Considère ceci:

double **hdl5 = malloc(3*sizeof(double*));
/* Error checking */
for (i=0; i<3; ++i){
   hdl5[i] = malloc(5*sizeof(double))
   /* Error checking */
}

Maintenant, hdl5 pointe vers un tableau de pointeurs, chacun d’eux pointant vers un tableau de doubles. Le bit cool est que vous pouvez utiliser la notation de tableau à deux dimensions pour accéder à cette structure ---hdl5[0][2] obtient l’élément central de la première ligne --- mais il s’agit néanmoins d’un type d’objet différent tableau dimensionnel déclaré par double ary[3][5];.

Cette structure est plus flexible qu'un tableau à deux dimensions (car les lignes ne doivent pas nécessairement avoir la même longueur), mais son accès sera généralement plus lent et nécessite plus de mémoire (vous avez besoin d'un emplacement pour contenir les pointeurs intermédiaires).

Notez que puisque je n’ai pas configuré de gardes, vous devrez vous-même connaître la taille de tous les tableaux. 

Arithmétique

c ne fournit aucun support pour le calcul vectoriel, matriciel ou tensoriel, vous devrez le mettre en œuvre vous-même ou importer une bibliothèque.

Il est facile de multiplier par un détartreur et d'additionner et de soustraire des tableaux du même rang: il suffit de passer en boucle sur les éléments et d'exécuter l'opération au fur et à mesure. Les produits intérieurs sont pareillement simples.

Les produits extérieurs signifient plus de boucles.

50
dmckee

Si vous connaissez le nombre de colonnes au moment de la compilation, c'est assez simple:

#define COLS ...
...
size_t rows;
// get number of rows
T (*ap)[COLS] = malloc(sizeof *ap * rows); // ap is a *pointer to an array* of T

Vous pouvez traiter ap comme n'importe quel tableau 2D:

ap[i][j] = x;

Lorsque vous avez terminé, vous le désallouez comme

free(ap);

Si vous ne connaissez pas le nombre de colonnes au moment de la compilation, mais que vous travaillez avec un compilateur C99 ou un compilateur C2011 qui prend en charge les tableaux de longueur variable, la procédure est simple:

size_t rows;
size_t cols;
// get rows and cols
T (*ap)[cols] = malloc(sizeof *ap * rows);
...
ap[i][j] = x;
...
free(ap);

Si vous ne connaissez pas le nombre de colonnes au moment de la compilation et que vous utilisez une version de C qui ne prend pas en charge les tableaux de longueur variable, vous devrez procéder de manière différente. Si vous avez besoin que tous les éléments soient alloués dans un bloc contigu (comme un tableau standard), vous pouvez alors allouer la mémoire sous forme de tableau 1D et calculer un offset 1D:

size_t rows, cols;
// get rows and columns
T *ap = malloc(sizeof *ap * rows * cols);
...
ap[i * rows + j] = x;
...
free(ap);

Si vous n'avez pas besoin que la mémoire soit contiguë, vous pouvez suivre une méthode d'allocation en deux étapes:

size_t rows, cols;
// get rows and cols
T **ap = malloc(sizeof *ap * rows);
if (ap)
{
  size_t i = 0;
  for (i = 0; i < cols; i++)
  {
    ap[i] = malloc(sizeof *ap[i] * cols);
  }
}

ap[i][j] = x;

L'attribution étant un processus en deux étapes, la désallocation doit également être un processus en deux étapes:

for (i = 0; i < cols; i++)
  free(ap[i]);
free(ap);
9
John Bode

Voici un code de travail qui définit un sous-programme make_3d_array pour allouer un tableau 3D multidimensionnel avec des éléments N1, N2 et N3 dans chaque dimension, puis le remplir avec des nombres aléatoires. Vous pouvez utiliser la notation A[i][j][k] pour accéder à ses éléments.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


// Method to allocate a 2D array of floats
float*** make_3d_array(int nx, int ny, int nz) {
    float*** arr;
    int i,j;

    arr = (float ***) malloc(nx*sizeof(float**));

    for (i = 0; i < nx; i++) {
        arr[i] = (float **) malloc(ny*sizeof(float*));

        for(j = 0; j < ny; j++) {
            arr[i][j] = (float *) malloc(nz * sizeof(float));
        }
    }

    return arr;
} 



int main(int argc, char *argv[])
{
    int i, j, k;
    size_t N1=10,N2=20,N3=5;

    // allocates 3D array
    float ***ran = make_3d_array(N1, N2, N3);

    // initialize pseudo-random number generator
    srand(time(NULL)); 

    // populates the array with random numbers
    for (i = 0; i < N1; i++){
        for (j=0; j<N2; j++) {
            for (k=0; k<N3; k++) {
                ran[i][j][k] = ((float)Rand()/(float)(Rand_MAX));
            }
        }
   }

    // prints values
    for (i=0; i<N1; i++) {
        for (j=0; j<N2; j++) {
            for (k=0; k<N3; k++) {
                printf("A[%d][%d][%d] = %f \n", i,j,k,ran[i][j][k]);
            }
        }
    }

    free(ran);
}
0
Rodrigo

malloc fera l'affaire.

 int rows = 20;
 int cols = 20;
 int *array;

  array = malloc(rows * cols * sizeof(int));

Reportez-vous à l'article ci-dessous pour obtenir de l'aide: -

http://courses.cs.vt.edu/~cs2704/spring00/mcquain/Notes/4up/Managing2DArrays.pdf

0
Rahul Tripathi