Supprimer les éléments de tableaux dynamiques
Donc, j'ai ceci:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void remove_element(int* array, int sizeOfArray, int indexToRemove)
{
int* temp = malloc((sizeOfArray - 1) * sizeof(int*)); //allocate an array with a size 1 less than the current one
memcpy(temp, array, indexToRemove - 1); //copy everything BEFORE the index
memcpy(temp+(indexToRemove * sizeof(int*)), temp+((indexToRemove+1) * sizeof(int*)), sizeOfArray - indexToRemove); //copy everything AFTER the index
free (array);
array = temp;
}
int main()
{
int howMany = 20;
int* test = malloc(howMany * sizeof(int*));
for (int i = 0; i < howMany; ++i)
(test[i]) = i;
printf("%d\n", test[16]);
remove_element(test, howMany, 16);
--howMany;
printf("%d\n", test[16]);
return 0;
}
Il est raisonnablement à l'auto-explicatif, remove_element supprime un élément d'un tableau dynamique.
Comme vous pouvez le voir, chaque élément de test est initialisé à une incrémentation entier (qui est, d'essai[n] == n). Toutefois, le programme des sorties
16
16
.
Après avoir supprimé un élément de test, on pourrait s'attendre à une appel à à un essai[n] où n >= l'élément supprimé aurait pour résultat de ce test[n+1] aurait été avant la suppression. Donc j'attendrais la sortie
16
17
. Ce qui ne va pas?
EDIT: Le problème a été résolu. Voici le code fixe (brut de débogage printfs), quelqu'un d'autre devrait le trouver utile:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int remove_element(int** array, int sizeOfArray, int indexToRemove)
{
printf("Beginning processing. Array is currently: ");
for (int i = 0; i < sizeOfArray; ++i)
printf("%d ", (*array)[i]);
printf("\n");
int* temp = malloc((sizeOfArray - 1) * sizeof(int)); //allocate an array with a size 1 less than the current one
memmove(
temp,
*array,
(indexToRemove+1)*sizeof(int)); //copy everything BEFORE the index
memmove(
temp+indexToRemove,
(*array)+(indexToRemove+1),
(sizeOfArray - indexToRemove)*sizeof(int)); //copy everything AFTER the index
printf("Processing done. Array is currently: ");
for (int i = 0; i < sizeOfArray - 1; ++i)
printf("%d ", (temp)[i]);
printf("\n");
free (*array);
*array = temp;
return 0;
}
int main()
{
int howMany = 20;
int* test = malloc(howMany * sizeof(int*));
for (int i = 0; i < howMany; ++i)
(test[i]) = i;
printf("%d\n", test[16]);
remove_element(&test, howMany, 14);
--howMany;
printf("%d\n", test[16]);
return 0;
}
Je pense que cnicutar repéré le problème exact, mais s'il vous plaît regardez ma réponse ainsi que pour certains plus importants commentaires.
simplement comme quelque chose à méditer: vous crédité d'une personne qui m'a répondu très rapidement, mais seulement attrapé une erreur dans votre code avec la solution. cependant, il y a une autre personne qui a posté seulement six minutes plus tard, ayant pris six différentes erreurs dans votre code. si vous aviez seulement avaient utilisé la réponse que vous avez donné de crédit, vous disposez d'au moins 5 problèmes importants avec votre code. Si c'était moi, je voudrais donner du crédit à la personne qui a pris son temps et a trouvé toutes les questions dans le code, plutôt que celui qui a posté le plus rapide. [pas de moi, j'étais beaucoup trop lent] just sayin'.
Je pense que vous avez raison. Et effectivement, je pense que votre réponse est la meilleure.
simplement comme quelque chose à méditer: vous crédité d'une personne qui m'a répondu très rapidement, mais seulement attrapé une erreur dans votre code avec la solution. cependant, il y a une autre personne qui a posté seulement six minutes plus tard, ayant pris six différentes erreurs dans votre code. si vous aviez seulement avaient utilisé la réponse que vous avez donné de crédit, vous disposez d'au moins 5 problèmes importants avec votre code. Si c'était moi, je voudrais donner du crédit à la personne qui a pris son temps et a trouvé toutes les questions dans le code, plutôt que celui qui a posté le plus rapide. [pas de moi, j'étais beaucoup trop lent] just sayin'.
Je pense que vous avez raison. Et effectivement, je pense que votre réponse est la meilleure.
OriginalL'auteur Dataflashsabot | 2011-08-17
Vous devez vous connecter pour publier un commentaire.
Je vois plusieurs problèmes dans la posté code, chaque de ce qui pourrait causer des problèmes:
retour de la new array
Votre fonction est de prendre un
int* array
mais alors vous essayez de changer votretemp
variable à la fin avant de renvoyer le nouveau tableau. Cela ne fonctionnera pas, que vous êtes tout simplement en remplacement de la copie locale deint* array
qui disparaîtra après le retour de la fonction.Vous avez besoin de passer votre pointeur sur le tableau dans un
int**
, qui vous permettra d'indiquer le pointeur vers le tableau dans la fonction, ou, je dirais juste retour d'une valeurde int* de votre fonction, et le retour de la nouvelle pile.
Aussi, comme mentionné dans cette réponse, vous n'avez même pas besoin de réaffecter lors de la suppression d'un élément du tableau, puisque le tableau d'origine est assez grand pour contenir tout.
de taille et d'offset des calculs
Vous utilisez
sizeof(int*)
pour le calcul de la matrice de la taille de l'élément. Cela peut fonctionner pour certains types, mais, par exemple, pour unshort
tableausizeof(short*)
ne fonctionne pas. Vous ne voulez pas la taille du pointeur vers le tableau, vous voulez que la taille des éléments, qui, pour votre exemple devrait êtresizeof(int)
bien qu'il ne peut pas causer des problèmes dans ce cas.Votre calcul de la longueur pour les décalages dans les tableaux semble ok, mais vous avez oublier de multiplier le nombre d'éléments par la taille de l'élément pour le paramètre de taille de la memcpy. par exemple,
memcpy(temp, array, indexToRemove * sizeof(int));
.Votre deuxième appel à memcpy est à l'aide de
temp
en plus de la compensation de la source de tableau, mais il devrait êtrearray
plus l'écart.Votre deuxième appel à memcpy est à l'aide de
sizeOfArray - indexToRemove
pour le nombre d'éléments à copier, mais vous ne devez copierSizeOfArray - indexToRemove - 1
éléments (ou(sizeOfArray - indexToRemove - 1) * sizeof(int)
octetsPartout où vous effectuez le calcul des décalages dans le temp et un tableau de tableaux, vous n'avez pas besoin de multiplier par sizeof(int), depuis l'arithmétique des pointeurs tient déjà compte de la taille des éléments. (J'ai raté cela à tout d'abord, merci à: cette réponse.)
regardant incorrect élément
L'impression de
test[16]
(17e) pour le test, mais vous êtes en train de supprimer le 16e élément, ce qui seraittest[15]
.coin de cas
Aussi (grâce à cette réponse), vous devez gérer le cas où
indexToRemove == 0
etindexToRemove == (sizeOfArray - 1)
, où vous pouvez faire de l'ensemble de la suppression dans un memcpy.Aussi, vous avez besoin de s'inquiéter à propos de l'affaire où
sizeOfArray == 1
. Dans ce cas, peut-être allouer 0, la taille du bloc de mémoire, ou de retourner la valeur null. Dans mon code mis à jour, j'ai choisi d'allouer un 0-la taille de bloc, juste pour différencier entre un tableau avec des 0 éléments contre un tableau non alloué.Retour d'un 0-tableau de taille signifie aussi il n'y a pas des modifications supplémentaires nécessaires pour le code, parce que les conditions avant chaque memcpy pour gérer les deux premiers cas mentionnés sera d'empêcher les memcpy.
Et juste de mentionner, il n'y a pas de gestion des erreurs dans le code, il y a donc implicitement les conditions pour que
indexToRemove
est dans les limites, quearray
n'est pas null, et quearray
a la taille passée en tant quesizeOfArray
.exemple de code mis à jour
quelques mots sur la gestion de la mémoire/les types de données abstraites
Enfin, quelque chose à considérer: il est possible à des questions à la fois avec l'aide de
malloc
à mémoire de retour à un utilisateur qui devrait êtrefree
d par l'utilisateur, et avecfree
ing mémoire qu'un utilisateurmalloc
ed. En général, il est moins probable que la gestion de la mémoire sera confuse et difficile à gérer si la conception de votre code d'unités telles que l'allocation de mémoire est gérée au sein d'une logique unique unité de code.Par exemple, vous pouvez créer un type abstrait de données module qui vous permet de créer un tableau d'entiers à l'aide d'une structure qui contient un pointeur et d'une longueur, et puis tout à la manipulation de ces données passe par des fonctions de la prise de la structure en tant que premier paramètre. Cela vous permet également, à l'exception de l'intérieur de ce module, pour éviter d'avoir à faire des calculs comme
elemNumber * sizeof(elemType)
. Quelque chose comme ceci:etc.
C'est essentiellement le fait de la mise en œuvre en C++des fonctions de type C, et c'est de l'OMI, une très bonne idée, surtout si vous partez de zéro et que vous voulez créer quelque chose de plus qu'une application très simple. Je sais que certains développeurs C qui n'aime vraiment pas ce langage, mais il a bien fonctionné pour moi.
La bonne chose à propos de cette façon de la mise en œuvre de choses, c'est que quelque chose dans votre code à l'aide de la fonction pour supprimer un élément serait de ne jamais être en contact avec le pointeur directement. Cela permettrait de différentes parties de votre code pour stocker un pointeur de votre résumé de la matrice de structure, et lorsque le pointeur vers les données de la matrice a été modifiée après que l'élément a été supprimé, toutes les variables pointant vers votre tableau abstrait serait automatiquement mis à jour.
En général, la gestion de la mémoire peut être très déroutant, et c'est une stratégie qui peut le rendre un peu moins. Juste une pensée.
OriginalL'auteur shelleybutterfly
Vous n'avez pas réellement changer le passé pointeur. Vous êtes en changeant seulement de votre copie de
array
.Ainsi, après la fonction de la matrice de points encore à où il est utilisé pour, MAIS vous avez également libéré, ce qui ajoute l'insulte à la blessure.
Essayer quelque chose comme cela:
Il y a aussi un C FAQ: Changement passé pointeur.
OriginalL'auteur cnicutar
@cnicutar est la droite (+1), mais aussi, vous écrivez:
alors qu'il devrait être:
Depuis la multiplication par la taille de
int*
est fait par le compilateur (c'est de l'arithmétique des pointeurs)Aussi, lors du déplacement de la superposition de zones de mémoire, utilisez
memmove
et pasmemcpy
.Je dirais que
memmove()
doit être utilisé en général, au cours dememcpy()
. Je serais prêt à parier que, dans la grande majorité des cas oùmemmove()
est utilisé avec les paramètres qui serait valable pourmemcpy()
, la différence de performances n'est même pas mesurable, beaucoup moins de toute conséquence. Même Linus Torvalds pourrait convenir: bugzilla.redhat.com/show_bug.cgi?id=638477#c101 (il n'y a que "l'appel à l'autorité"). Cependant, j'admets souvent de ne pas suivre mes propres conseils sur cette question...Burr - je suis totalement d'accord. Et je ne suis pas la suite que toujours 🙂
Je dois ajouter que, d'un temps où
memcpy()
est sans doute légitime de préférer au cours dememmove()
, c'est quand vous vous attendez à profiter d'un "intrinsèque" compilateur de mise en œuvre.memcpy()
peut être plus facilement optimisé alorsmemmove()
parce qu'il n'a pas à s'inquiéter de savoir où l'éditeur de liens peut placer des objets.OriginalL'auteur MByD
Plus loin: le deuxième argument de votre deuxième
memcpy
appel doit être fondée surarray
, pas surtemp
, droit? Et ne devriez-vous pas être mallocing et la copie basée sursizeof int
et ne repose pas sursizeof int*
, depuis votre tableau de stocker des entiers et non des pointeurs? Et n'avez-vous pas besoin de multiplier le nombre d'octets que vous êtes à la reproduction (le dernier argument dememcpy
) parsizeof int
?Aussi, regarder le cas où
indexToRemove == 0
.OriginalL'auteur Tim Smith
Il y a quelques problèmes avec ce code :
(a) Lors de l'allocation de la mémoire, vous devez vous assurer d'utiliser le bon type avec
sizeof
. Pour un tableau deint
eg., vous allouer un bloc de mémoire avec une taille qui est un multiple desizeof(int)
. Donc :devrait être :
(b) Vous n'avez pas de libérer la mémoire pour le tableau à la fin de
main
.(c)
memcpy
prend le nombre d'octets à copier en tant que troisième paramètre. Vous devez donc, à nouveau, assurez-vous de passer un multiple desizeof(int)
. Donc :devrait être :
(d) lors de la copie d'éléments de l'ancien tableau à la nouvelle pile, assurez-vous de copier les données correctes. Par exemple, il y a
indexToRemove
éléments avant de l'élément à l'indexindexToRemove
, pas un de moins. De même, vous aurez besoin pour vous assurer que vous avez une copie du bon montant, après l'élément qui doit être retiré.(e) Lors de l'incrémentation d'un pointeur, vous n'avez pas besoin de multiplier avec
sizeof(int)
- c'est ce que fait implicitement pour vous. Donc :devrait vraiment être :
(f) Dans votre
remove_element
fonction, vous pouvez assigner une valeur à la variable localearray
. Tout changement de variables locales ne sont pas visibles à l'extérieur de la fonction. Donc, après l'appel àremove_element
se termine, vous ne pourrez pas voir le changement dansmain
. Une façon de résoudre ce problème, il est de retour le nouveau pointeur de la fonction, et de l'assigner dansmain
:OriginalL'auteur Sander De Dycker
Toutes les autres réponses de bons points sur les divers problèmes/bugs dans le code.
Mais, pourquoi réaffecter à tous (et pas que les bugs sont toutes liées à la redistribution)? Le "plus petit" tableau ajustement très bien dans le bloc de mémoire:
ce n'est certainement pas une bonne fonction à utiliser pour supprimer un tas d'éléments; je ne cherche pas à résoudre tous les problèmes possibles. Gardez à l'esprit que vous pouvez presque toujours trouver des scénarios où un algorithme se comporte mal le truc c'est d'essayer de minimiser ou de gérer ces situations. Je serais prêt à parier que la réaffectation des sur chaque retirez probablement se comporte pire en plus des habitudes de consommation. Si vous avez besoin de supprimer un tas d'éléments, une approche pourrait être de supprimer les éléments sans réaffectation, puis réaffecter et copie du tableau final une fois si vous voulez réduire le bloc de mémoire utilisée par le tableau.
Aussi - si je peux utiliser le "recours à l'autorité' erreur de logique: C++
std::vector
se comporte de cette façon, elle ne sera pas réaffecter le bloc de mémoire que les éléments du vecteur sont dans quand un ou plusieurs des éléments sont supprimés. Un utilisateur qui veut "rétrécir" la mémoire utilisée par unstd::vector
doit prendre les mesures nécessaires pour le faire.+1, car il va s'adapter, et c'est un bon langage pour en savoir
OriginalL'auteur Michael Burr