J'utilise malloc pour allouer dynamiquement un tableau de caractères 2D. À moins de définir chaque index de tableau sur NULL avant free () , j'obtiens un défaut de segmentation en essayant de free () . Pourquoi ai-je une erreur de segmentation?
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int nrows = 20; int ncolumns = 10;
char ** arr = malloc(nrows * sizeof(char *));
for(int i = 0; i < nrows; i++)
arr[i] = malloc(ncolumns * sizeof(char));
arr[0] = "string1";
arr[1] = "string2";
// does not work without the following code:
// for(int i = 0; i < 20; i++)
// arr[i] = NULL;
for(int i = 0; i < 20; i++)
free(arr[i]);
free(arr);
return 0;
}
5 Réponses :
= ne copie pas la chaîne, il affecte uniquement le pointeur. Donc, votre mémoire malléable n'est plus accessible pour ces éléments de tableau et tenter de la libérer est un comportement indéfini qui peut conduire à l'erreur de segmentation.
Vous devez le copier en utilisant strcpy .
La troisième réponse qui suggère d'utiliser le strcpy () extrêmement dangereux. Plus tôt nous oublions que strcpy () a jamais existé, mieux c'est. Utilisez plutôt strdup () .
@cmaster ce n'est évidemment pas la vérité. N'essayez pas d'utiliser strdup lors de la programmation de microcontrôleurs.
en passant, utilisez strncpy au lieu de strcpy: pour éviter un débordement de tampon.
Si vous utilisez strncpy , assurez-vous également de définir explicitement le dernier caractère sur un terminateur nul. linux.die.net/man/3/strncpy
strncpy est vraiment dangereux :)
Ok, strcpy () est là pour rester pour les cas d'utilisation où nous sommes obligés de l'utiliser. Cependant, c'est une très mauvaise habitude d'utiliser strcpy () partout où cela semble aller. Ce ne devrait être qu'un dernier recours, et même dans ce cas, chaque utilisation de strcpy () nécessite une preuve écrite dans les commentaires de la raison pour laquelle son utilisation est légitime. Pensez simplement au nombre de hacks rendus possibles grâce à l'utilisation de strcpy () et d'amis!
Lorsque vous faites cela:
strcpy(arr[0], "string1"); strcpy(arr[1], "string2");
Vous écrasez le contenu de arr [0] et arr [1] , qui contiennent les adresses de mémoire renvoyées par malloc , avec l'adresse de deux constantes chaîne. Cela provoque une fuite de mémoire, car cette mémoire n'est plus accessible. C'est aussi la raison pour laquelle vous plantez lorsque vous appelez free parce que ces variables ne contiennent plus les adresses de la mémoire allouée.
Ce que vous voudrez probablement faire ici à la place est d'utiliser strcpy , qui copiera le contenu de la chaîne littérale dans la mémoire que vous avez allouée.
arr[0] = "string1"; arr[1] = "string2";
Vous pouvez maintenant libérer la mémoire correctement .
Non. N'utilisez pas strcpy () . Il remplira volontiers vos tampons. Utilisez plutôt strdup () .
@cmaster strcpy convient si vous savez à quoi vous avez affaire, c'est-à-dire que la source est une constante de chaîne et que vous connaissez la taille de la destination. strdup peut ne pas être approprié en fonction de la situation, et avec strncpy vous devez ajouter manuellement l'octet nul à la fin si la source est trop grande.
C'est précisément ce que le PO ne semble pas savoir. Je sais que je peux utiliser strcpy () de manière relativement sûre. Mais cette capacité ne vient qu'au prix de ne jamais vouloir l'utiliser, à moins d'être forcé. Si vous souhaitez toujours utiliser strcpy () sur strdup () , vous n'avez tout simplement pas encore appris la leçon.
Deux choses à savoir:
La règle pour chaque ressource que vous obtenez de malloc vous devez la libérer. pour libérer appelez simplement la fonction libre (mais nous pouvons éventuellement attribuer un pointeur nul pour être sûr qu'il est libéré).
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int nrows = 20; int ncolumns = 10;
char ** arr = malloc(nrows * sizeof(char *));
for(int i = 0; i < nrows; i++)
arr[i] = malloc(ncolumns * sizeof(char));
free(arr[0]);//know we can loose it value because it is freed
arr[0] = NULL;// in fact we assign a value just after so this line is useless but is educationnal purpose
free(arr[1]);//know we can loose it value because it is freed
arr[1] = NULL;// in fact we assign a value just after so this line is useless but is educationnal purpose
arr[0] = "string1";
arr[1] = "string2";
// does not work without the following code:
// for(int i = 0; i < 20; i++)
// arr[i] = NULL;
for(int i = 2; i < 20; i++)//we start at 2 because the first two value are on the stack
{
free(arr[i]);
arr[i] = NULL;//this is useless because we will free arr just after the loop)
}
free(arr);
arr = NULL;// this is useless because we exit the end of program
return 0;
}
pour libérer
free(a);/* this is mandatory */ a = NULL;/* we can add this to the first line */
Votre code est correct, le problème vient du fait que vous attribuez ici des chaînes littéraux à votre tableau: arr [0] = "string1"; .
Vous remplacez ainsi le pointeur sur arr [0] , qui pointe vers la mémoire allouée, par le pointeur vers une chaîne littérale.
Les pointeurs vers les littéraux sont protégés , vous ne pouvez pas les libérer (ni leur écrire) car vous ne les avez pas alloués.
Pour résoudre ce problème, utilisez strcpy pour copier la valeur de votre littéral dans votre alloué mémoire:
strcpy(arr[0], "string1"); strcpy(arr[1], "string2");
Même problème qu'avec la réponse de dbush: strcpy () est très dangereux et ne doit pas être utilisé sans preuve d'utilisation correcte. Utilisez plutôt strdup () , et vous vous épargnerez de nombreux maux de tête.
Un crash lors d'un appel à free est le signe d'une mauvaise gestion de la mémoire ailleurs dans votre code. Lorsque vous définissez un pointeur sur NULL puis sur free , vous n'allez pas planter, car free (NULL) est garanti inoffensif par la norme C § 7.22.3.3:
7.22.3.3 La fonction libre
... Si ptr est un pointeur nul, aucune action ne se produit . Sinon, si l'argument ne correspond pas à un pointeur précédemment renvoyé par une gestion mémoire fonction , ou si l'espace a été libéré par un appel à free ou à réallouer, le le comportement n'est pas défini.
Insistez sur moi.
Comme d'autres réponses l'ont noté, vous essayez d'appeler free sur une mémoire que vous n'avez pas explicitement allouée avec des fonctions familiales malloc (depuis que vous avez écrasé arr [i] pointeurs avec des pointeurs vers des chaînes littérales)
arr [0] = "string1";Vous réaffectez le pointeur. Utilisez plutôtstrcpy ().Notez que [comme d'autres l'ont mentionné], vous avez besoin (par exemple) de
strcpy (arr [0], "string1");Mais, notez que"string1"doit être non plus de 9 caractères (c'est-à-direncolonnes - 1). Donc sois prudent. Si votre deuxième boucle a fait:arr [i] = NULL;, vous pouvez faire:arr [0] = strdup ("string1");comme alternative. Ensuite, votre boucle libre pourrait faire:if (arr [i]! = NULL) free (arr [i]);Fairestrdupici est généralement mieux car cela permet string dans le tableau pour avoir la longueur dont il a besoin vs être contraint à une longueur fixe. Ainsi, il accueille des chaînes plus courtes ou plus longues avec un minimum de mémoire gaspillée@CraigEstey nous avons donc besoin que dans son cas arr [i] point à empiler donc il! = NULL et nous pouvons libérer librement un pinter NULL (ça ne fait rien) peut-être que vous voulez stocker votre texte dans le premier arr [0] alors utilisez strncpy au lieu de strcpy (recommandé par d'autres): pour éviter un débordement de tampon.
@ Et7f3XIV Oui,
free (NULL)est inoffensif. Mais ça n'a pas toujours été le cas, alors parfois j'oublie. Mais, je ne ferais pas non plusstrcpy / strncpymais, comme je l'ai dit,strdup. Sinon, lechar ** arrn'a pas d'intérêt. Nous ferions:char arr [nrows] [ncolumns]etstrcpy / strncpy. Je suis surpris par les suggestions d'utiliserstrncpycar d'autres répondants sur d'autres pages ont dit ne pas l'utiliser [sans (par exemple) ajouter / forcer le terminateur EOS après le appel].Ce n'est pas un tableau 2D. C'est un tableau de pointeurs vers un groupe de tableaux 1d. Consultez Allocation correcte de tableaux multidimensionnels .
@Craig Estey mon commentaire sur strncpy est pour l'exemple que vous avez donné (si nous n'avons pas de limite, nous pouvons avoir un dépassement de tampon qui est une faille de sécurité, c'est comme utiliser gets au lieu de fgets en 2019). Et vous pouvez voir dans ma réponse que je libère après les 2 premières cellules (mais vous avez raison, il est plus pratique de libérer un pointeur et de stocker le pointeur retourné par strdup). J'ajouterai +1 pour vous ^^