Dans un projet, quelqu'un a poussé cette ligne:
double (*e)[n+1] = malloc((n+1) * sizeof(*e));
Ce qui crée supposément un tableau bidimensionnel de (n + 1) * (n + 1) doubles.
Soi -disant, dis-je, parce que jusqu'à présent, personne à qui j'ai demandé ne pouvait me dire ce que cela faisait exactement, ni d'où cela venait ni pourquoi cela devrait fonctionner (ce qui serait prétendument le cas, mais je ne l'achète pas encore).
Peut-être que je manque quelque chose d'évident, mais j'apprécierais que quelqu'un puisse m'expliquer ci-dessus. Parce que personnellement, je me sentirais beaucoup mieux si nous utilisions quelque chose que nous comprenons réellement.
c
arrays
multidimensional-array
malloc
allocation
Utilisateur1291
la source
la source
n+1
mais à la placedouble (*e)[rows] = malloc(columns * sizeof *e);
Réponses:
La variable
e
est un pointeur vers un tableau d'n + 1
éléments de typedouble
.L'utilisation de l'opérateur de déréférence sur
e
vous donne le type de basee
dont est "tableau d'n + 1
éléments de typedouble
".L'
malloc
appel prend simplement le type de base dee
(expliqué ci-dessus) et obtient sa taille, la multiplie parn + 1
et en passant cette taille à lamalloc
fonction. Allouant essentiellement un tableau den + 1
tableaux d'n + 1
éléments dedouble
.la source
sizeof(*e)
équivaut àsizeof(double [n + 1])
. Multipliez cela avecn + 1
et vous en avez assez.n+1
deux dimensions, le résultat sera carré. Si vous le faitesdouble (*e)[cols] = malloc(rows * sizeof(*e));
, le résultat aura le nombre de lignes et de colonnes que vous avez spécifié.int rows = n+1
etint cols = n+1
. Dieu nous sauve du code intelligent.C'est la manière habituelle d'allouer des tableaux 2D de manière dynamique.
e
est un pointeur de tableau vers un tableau de typedouble [n+1]
.sizeof(*e)
donne donc le type du type pointé, qui est la taille d'undouble [n+1]
tableau.n+1
ces tableaux.e
pour qu'il pointe sur le premier tableau de ce tableau de tableaux.e
ase[i][j]
pour accéder à des éléments individuels dans le tableau 2D.Personnellement, je pense que ce style est beaucoup plus facile à lire:
la source
ptr = malloc(sizeof *ptr * count)
style.malloc(row*col*sizeof(double))
se produit en cas derow*col*sizeof()
débordement, mais cesizeof()*row*col
n'est pas le cas. (eg row, col areint
)sizeof *e * (n+1)
est plus facile à maintenir; si jamais vous décidez de changer le type de base (dedouble
àlong double
, par exemple), il vous suffit de changer la déclaration dee
; vous n'avez pas besoin de modifier l'sizeof
expression dans l'malloc
appel (ce qui vous fait gagner du temps et vous évite de la changer à un endroit mais pas à l'autre).sizeof *e
vous donnera toujours la bonne taille.Cet idiome sort naturellement de l'allocation de tableau 1D. Commençons par allouer un tableau 1D d'un type arbitraire
T
:Simple, non? L' expression
*p
a un typeT
, doncsizeof *p
donne le même résultat quesizeof (T)
, donc nousN
allouons suffisamment d'espace pour un tableau -element deT
. Cela est vrai pour tout typeT
.Maintenant, remplaçons
T
par un type de tableau commeR [10]
. Alors notre allocation devientLa 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 deT *
, c'est maintenantR (*)[10]
. L'expression*p
a un typeT
qui est typeR [10]
, doncsizeof *p
est équivalent àsizeof (T)
qui est équivalent àsizeof (R [10])
. Nous allouons donc suffisamment d'espace pour un tableauN
par10
élément deR
.Nous pouvons aller encore plus loin si nous le voulons; suppose
R
est lui-même un type de tableauint [5]
. Remplacez celaR
et nous obtenonsMême accord -
sizeof *p
est le même quesizeof (int [10][5])
, et nous finissons par allouer un morceau de mémoire contigu assez grand pour contenir unN
par10
par5
tableau deint
.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. Sip
est un pointeur versT
, vous pouvez accéder à la valeur pointée soit en déréférençant explicitement avec l'*
opérateur unaire :ou en utilisant l'
[]
opérateur indice:Ainsi, si
p
pointe 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 pointeurp
:Maintenant, refaisons notre opération de substitution et remplaçons
T
par le type de tableauR [10]
:Une différence immédiatement apparente; nous déréférencons explicitement
p
avant d'appliquer l'opérateur indice. Nous ne voulons pas souscrirep
, nous voulons indiquer ce quip
pointe vers (dans ce cas, le tableauarr[0]
). Étant donné que l'unaire*
a une priorité inférieure à l'[]
opérateur indice , nous devons utiliser des parenthèses pour regrouper explicitementp
avec*
. Mais rappelez-vous d'en haut que*p
c'est la même chose quep[0]
, donc nous pouvons le remplacer parou juste
Ainsi, si
p
pointe vers un tableau 2D, nous pouvons indexer ce tableaup
comme suit:Prenant cela à la même conclusion que ci-dessus et en remplaçant
R
parint [5]
:Cela fonctionne exactement de la même manière s'il
p
pointe vers un tableau normal ou s'il pointe vers la mémoire allouée viamalloc
.Cet idiome présente les avantages suivants:
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.
la source