110 votes

Façon bizarre d’attribuer le tableau à deux dimensions ?

Dans un projet, quelqu'un a poussé cette ligne:

double (*e)[n+1] = malloc((n+1) * sizeof(*e));

Qui, soit disant, crée un tableau à deux dimensions de (n+1)*(n+1) en double.

Soi-disant, je le dis, parce que jusqu'à présent, personne à qui je demande pourrait me dire ce qu'il fait, exactement, ni d'où elle provient ou pourquoi il devrait fonctionner (ce qui aurait fait, mais je ne suis pas encore acheter).

Peut-être que je suis absent quelque chose d'évident, mais je l'apprécierais si quelqu'un pourrait-il expliquer au-dessus de la ligne pour moi. Parce que personnellement, je me sentirais beaucoup mieux si nous avions quelque chose de l'utilisation que nous en fait comprendre.

87voto

Joachim Pileborg Points 121221

La variable e est un pointeur vers un tableau d' n + 1 pour les éléments de type double.

À l'aide de l'opérateur de déréférencement sur e vous donne la base type d' e qui est " tableau d' n + 1 pour les éléments de type double".

L' malloc appelez simplement à la base type d' e (expliqué ci-dessus) et obtient de sa taille, la multiplie par n + 1, et en passant que la taille de l' malloc fonction. Essentiellement de l'allocation d'un tableau de n + 1 tableaux de n + 1 éléments double.

56voto

Lundin Points 21616

C'est la manière typique, vous devez allouer de la 2D tableaux dynamiquement.

  • e est un tableau de pointeur vers un tableau de type double [n+1].
  • sizeof(*e) donc donne le type de la pointe-au type, qui est de la taille d'un double [n+1] tableau.
  • Allouer de la place pour n+1 ces tableaux.
  • Vous définissez le tableau de pointeur e de point de le premier tableau, dans ce tableau de tableaux.
  • Cela vous permet d'utiliser e comme e[i][j] d'accéder à différents éléments dans un tableau 2D.

Personnellement, je pense que ce style est beaucoup plus facile à lire:

double (*e)[n+1] = malloc( sizeof(double[n+1][n+1]) );

39voto

John Bode Points 33046

Cet idiome tombe naturellement de 1D allocation de tableau. Commençons par l'allocation d'un tableau 1D de certains type arbitraire T:

T *p = malloc( sizeof *p * N );

Simple, non? L' expression *p type T, alors sizeof *p donne le même résultat que l' sizeof (T), nous sommes donc d'allouer assez d'espace pour un N-élément de tableau de T. Cela est vrai pour n'importe quel type T.

Maintenant, nous allons remplacer T avec un type tableau comme R [10]. Ensuite, notre allocation devient

R (*p)[10] = malloc( sizeof *p * N);

La sémantique ici sont exactement les mêmes que les 1D méthode de répartition; tout ce qui a changé, c'est le type d' p. Au lieu de T *, il est maintenant de R (*)[10]. L'expression *p type T qui est de type R [10], alors sizeof *p est équivalent à sizeof (T) ce qui est équivalent à sizeof (R [10]). Nous sommes donc d'allouer assez d'espace pour un N par 10 élément de tableau de R.

Nous pouvons prendre encore plus loin si nous voulons; supposons R est lui-même un tableau de type int [5]. Un substitut pour R et nous obtenons

int (*p)[10][5] = malloc( sizeof *p * N);

De même, le face - sizeof *p est le même que sizeof (int [10][5]), et nous avons le vent en place d'allouer un espace contigu de mémoire assez grande pour contenir une N par 10 par 5 tableau d' int.

Donc, c'est la répartition de côté; ce que sur le côté d'accès?

Rappelez-vous que l' [] indice fonctionnement est défini dans les termes de l'arithmétique des pointeurs: a[i] est défini comme *(a + i)1. Ainsi, l'indice de l'opérateur [] implicitement déréférence un pointeur. Si p est un pointeur vers T, vous pouvez accéder à la pointue de valeur, soit en faisant explicitement référence à l'unaire * opérateur:

T x = *p;

ou en utilisant l' [] indice de l'opérateur:

T x = p[0]; // identical to *p

Ainsi, si p points pour le premier élément d'un tableau, vous pouvez accéder à tout élément de ce tableau à l'aide d'un indice sur le pointeur p:

T arr[N];
T *p = arr; // expression arr "decays" from type T [N] to T *
...
T x = p[i]; // access the i'th element of arr through pointer p

Maintenant, nous allons faire de notre opération de substitution de nouveau et remplacez - T avec le type du tableau R [10]:

R arr[N][10];
R (*p)[10] = arr; // expression arr "decays" from type R [N][10] to R (*)[10]
...
R x = (*p)[i];

Une différence immédiatement apparente; nous sommes explicitement référence p avant l'application de l'indice de l'opérateur. Nous ne voulons pas d'indice en p, nous voulons indice en ce qui p de points à (dans ce cas, la matrice arr[0]). Depuis unaire * a priorité moins élevée que l'indice [] de l'opérateur, il faut utiliser des parenthèses pour explicitement groupe p avec *. Mais rappelez-vous, d'en haut, *p est le même que p[0], donc on peut remplacer qu'avec

R x = (p[0])[i];

ou tout simplement

R x = p[0][i];

Ainsi, si p de points à un tableau 2D, nous pouvons indice dans ce tableau via p comme:

R x = p[i][j]; // access the i'th element of arr through pointer p;
               // each arr[i] is a 10-element array of R

La prise de ce à la même conclusion que ci-dessus et de le remplacer par R avec int [5]:

int arr[N][10][5];
int (*p)[10][5]; // expression arr "decays" from type int [N][5][10] to int (*)[10][5]
...
int x = p[i][j][k];

Cela fonctionne tout de même si p de points à un réseau régulier, ou si elle pointe vers la mémoire allouée par le biais malloc.

Cet idiome a les avantages suivants:

  1. C'est simple - il suffit d'une seule ligne de code, par opposition à l'fragmentaire de la méthode de répartition
    T **arr = malloc( sizeof *arr * N );
    if ( arr )
    {
      for ( size_t i = 0; i < N; i++ )
      {
        arr[i] = malloc( sizeof *arr[i] * M );
      }
    }
    
  2. Toutes les lignes de la répartition de la matrice sont *contigus*, ce qui n'est pas le cas avec le fragmentaire de la méthode de répartition ci-dessus;
  3. Libérer le tableau est tout aussi facile avec un seul appel à free. Encore une fois, pas vrai avec le fragmentaire de la méthode de répartition, où vous devez libérer chaque arr[i] avant de pouvoir libérer arr.

Parfois fragmentaires, de la méthode de répartition est préférable, comme quand votre tas est très fragmenté et vous ne pouvez pas allouer la mémoire comme un espace contigu, ou que vous souhaitez allouer un "irréguliers" tableau où chaque ligne peut avoir une longueur différente. Mais en général, c'est la meilleure façon d'aller.


1. Rappelez-vous que les tableaux ne sont pas des pointeurs - au lieu de cela, tableau expressions sont convertis pointeur expressions comme nécessaire.

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