Cet idiome sort naturellement de l'allocation de tableau 1D. Commençons par allouer un tableau 1D d'un type arbitraire T:
T *p = malloc( sizeof *p * N );
Simple, non? L' expression *p a un type T, donc sizeof *pdonne le même résultat que sizeof (T), donc nous Nallouons suffisamment d'espace pour un tableau -element de T. Cela est vrai pour tout typeT .  
Maintenant, remplaçons Tpar un type de tableau comme R [10]. Alors notre allocation devient
R (*p)[10] = malloc( sizeof *p * N);
La sémantique ici est exactement la même que celle de la méthode d'allocation 1D; tout ce qui a changé est le type de p. Au lieu de T *, c'est maintenant R (*)[10]. L'expression *pa un type Tqui est type R [10], donc sizeof *pest équivalent à sizeof (T)qui est équivalent à sizeof (R [10]). Nous allouons donc suffisamment d'espace pour un tableau Npar 10élément de R.  
Nous pouvons aller encore plus loin si nous le voulons; suppose Rest lui-même un type de tableau int [5]. Remplacez cela Ret nous obtenons
int (*p)[10][5] = malloc( sizeof *p * N);
Même accord - sizeof *pest le même que sizeof (int [10][5]), et nous finissons par allouer un morceau de mémoire contigu assez grand pour contenir un Npar 10par 5tableau de int.  
Voilà donc le côté allocation; qu'en est-il du côté accès?
N'oubliez pas que l' []opération d'indice est définie en termes d'arithmétique de pointeur: a[i]est définie comme *(a + i)1 . Ainsi, l'opérateur d'indice déréférence [] implicitement un pointeur. Si pest un pointeur vers T, vous pouvez accéder à la valeur pointée soit en déréférençant explicitement avec l' *opérateur unaire :
T x = *p;
ou en utilisant l' []opérateur indice:
T x = p[0]; // identical to *p
Ainsi, si ppointe sur le premier élément d'un tableau , vous pouvez accéder à n'importe quel élément de ce tableau en utilisant 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, refaisons notre opération de substitution et remplaçons Tpar le type de 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 déréférencons explicitement pavant d'appliquer l'opérateur indice. Nous ne voulons pas souscrire p, nous voulons indiquer ce qui p pointe vers (dans ce cas, le tableau arr[0] ). Étant donné que l'unaire *a une priorité inférieure à l' []opérateur indice , nous devons utiliser des parenthèses pour regrouper explicitement pavec *. Mais rappelez-vous d'en haut que *pc'est la même chose que p[0], donc nous pouvons le remplacer par
R x = (p[0])[i];
ou juste
R x = p[0][i];
Ainsi, si ppointe vers un tableau 2D, nous pouvons indexer ce tableau pcomme suit:
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
Prenant cela à la même conclusion que ci-dessus et en remplaçant Rpar 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 exactement de la même manière s'il ppointe vers un tableau normal ou s'il pointe vers la mémoire allouée via malloc.  
Cet idiome présente les avantages suivants:
- C'est simple - juste une ligne de code, par opposition à la méthode d'allocation fragmentaireT **arr = malloc( sizeof *arr * N );
if ( arr )
{
  for ( size_t i = 0; i < N; i++ )
  {
    arr[i] = malloc( sizeof *arr[i] * M );
  }
}
 
- Toutes les lignes du tableau alloué sont * contiguës *, ce qui n'est pas le cas avec la méthode d'allocation fragmentaire ci-dessus;
- Désallouer le tableau est tout aussi simple avec un seul appel à free. Encore une fois, ce n'est pas vrai avec la méthode d'allocation au coup par coup, où vous devez désallouer chacunarr[i]avant de pouvoir désallouerarr.
Parfois, la méthode d'allocation fragmentaire est préférable, par exemple lorsque votre tas est gravement fragmenté et que vous ne pouvez pas allouer votre mémoire en tant que bloc contigu, ou que vous souhaitez allouer un tableau "en dents de scie" où chaque ligne peut avoir une longueur différente. Mais en général, c'est la meilleure façon de procéder.  
1. N'oubliez pas que les tableaux ne sont pas des pointeurs - au lieu de cela, les expressions de tableau sont converties en expressions de pointeur si nécessaire.