Question

Quelqu'un m'a récemment fait remarquer dans un de mes codes que j'utilise

char* name = malloc(256*sizeof(char));
// more code
free(name);

J'avais l'impression que cette façon de configurer un tableau était identique à l'utilisation de

char name[256];

et que les deux sens nécessiteraient l'utilisation de free (). Est-ce que je me trompe et, dans l'affirmative, quelqu'un pourrait-il expliquer, en termes simples, quelle est la différence?

Était-ce utile?

La solution

Dans le premier code, la mémoire est allouée dynamiquement sur le tas. Cette mémoire doit être libérée avec free (). Sa durée de vie est arbitraire: elle peut dépasser les limites des fonctions, etc.

Dans le deuxième code, les 256 octets sont alloués sur la pile et sont automatiquement récupérés au retour de la fonction (ou à la fin du programme si elle se trouve en dehors de toutes les fonctions). Donc, vous n'avez pas (et ne pouvez pas) appeler free () dessus. Il ne peut pas couler, mais il ne vivra pas plus longtemps.

Choisissez entre les deux en fonction des besoins en mémoire.

Addendum (Pax):

Si je peux ajouter quelque chose à cela, Ned, la plupart des implémentations fourniront généralement plus de tas que de piles (au moins par défaut). Cela ne sera généralement pas important pour 256 octets, sauf si vous êtes déjà à court de pile ou si vous faites des choses très récursives.

De plus, sizeof (char) correspond toujours à 1 selon la norme, vous n'avez donc pas besoin de cette multiplication superflue. Même si le compilateur va probablement l'optimiser, il rend le code laid IMNSHO.

Mettez fin à l’addenda (Pax).

Autres conseils

  

et que, dans les deux cas, l'utilisation de free () sera nécessaire.

Non, seul le premier nécessite l'utilisation d'un logiciel gratuit. La seconde est allouée sur la pile. Cela le rend incroyablement rapide à allouer. Regardez ici:

void doit() {
    /* ... */
    /* SP += 10 * sizeof(int) */
    int a[10];
    /* ... (using a) */

} /* SP -= 10 */

Lorsque vous le créez, le compilateur au moment de la compilation connaît sa taille et allouera la taille appropriée à la pile. La pile est une grande partie de la mémoire continue située quelque part. Mettre quelque chose sur la pile incrémentera (ou diminuera selon votre plate-forme) le pointeur de pile. Sortir de la portée fera l'inverse et votre tableau sera libéré. Cela se produira automatiquement. Les variables ainsi créées ont une durée de stockage automatique .

Utiliser Malloc est différent. Il ordonnera une grande quantité de mémoire arbitraire (à partir d'un emplacement appelé freestore ). Le moteur d’exécution devra rechercher un bloc de mémoire assez volumineux. La taille peut être déterminée au moment de l'exécution. Par conséquent, le compilateur ne peut généralement pas l'optimiser au moment de la compilation. Comme le pointeur peut sortir du champ d'application ou être copié, il n'y a pas de couplage inhérent entre la mémoire allouée et le pointeur auquel l'adresse de mémoire est affectée. La mémoire est donc toujours allouée même si vous avez quitté la fonction il y a longtemps. . Vous devez téléphoner gratuitement en passant l'adresse que vous avez obtenue manuellement de malloc si le moment est venu de le faire.

Certains " récents " La forme de C, appelée C99, vous permet de donner aux tableaux une taille d’exécution. Vous êtes autorisé à faire:

void doit(int n) {
    int a[n]; // allocate n * sizeof(int) size on the stack */
}

Mais cette fonctionnalité devrait être évitée si vous n’avez pas de raison de l’utiliser. Une des raisons est que ce n'est pas sûr: si aucune mémoire n'est disponible, tout peut arriver. Une autre est que C99 n’est pas très portable parmi les compilateurs.

Il existe une troisième possibilité, à savoir que le tableau puisse être déclaré externe à une fonction, mais de manière statique, par exemple,

// file foo.c
char name[256];

int foo() {
    // do something here.
}

J'ai été plutôt surpris par les réponses à une autre question sur SO que quelqu'un trouvait cela inapproprié en C; voici ce n'est même pas mentionné, et je suis un peu confus et surpris (comme "qu'enseignent-ils aux enfants à l'école ces jours-ci?") à ce sujet.

Si vous utilisez cette définition, la mémoire est allouée de manière statique, ni sur le tas ni sur la pile, mais dans l'espace de données de l'image. Ainsi, il ne faut ni gérer comme avec malloc / free, ni vous soucier de la réutilisation de l’adresse comme vous le feriez avec une définition automatique.

Il est utile de rappeler l'ensemble " déclaré " vs " défini " chose ici. Voici un exemple

/* example.c */

char buf1[256] ;           /* declared extern, defined in data space */
static char buf2[256] ;    /* declared static, defined in data space */
char * buf3 ;              /* declared extern, defined one ptr in data space */
int example(int c) {       /* c declared here, defined on stack */
    char buf4[256] ;       /* declared here, defined on stack   */
    char * buf5 = malloc(256)]   /* pointer declared here, defined on stack */
                           /* and buf4 is address of 256 bytes alloc'd on heap */
    buf3 = malloc(256);    /* now buf3 contains address of 256 more bytes on heap */

    return 0;              /* stack unwound; buf4 and buf5 lost.      */
                           /* NOTICE buf4 memory on heap still allocated */
                           /* so this leaks 256 bytes of memory */
}

Maintenant dans un tout autre fichier

/* example2.c */

extern char buf1[];             /* gets the SAME chunk of memory as from example.c */
static char buf2[256];          /* DIFFERENT 256 char buffer than example.c */
extern char * buf3 ;            /* Same pointer as from example.c */
void undoes() {
     free(buf3);                /* this will work as long as example() called first */
     return ;
}

Ceci est incorrect - la déclaration du tableau ne nécessite pas de free. De plus, si cela se trouve dans une fonction, elle est allouée sur la pile (si la mémoire le permet) et est automatiquement relâchée avec les retours de fonction; ne renvoyez pas de référence à l'appelant!

Décomposez votre déclaration

char* name = malloc(256*sizeof(char)); // one statement
char *name; // Step 1 declare pointer to character
name = malloc(256*sizeof(char)); // assign address to pointer of memory from heap
name[2]; // access 3rd item in array
*(name+2); // access 3rd item in array
name++; // move name to item 1

Traduction: le nom est maintenant un pointeur sur un caractère auquel est attribuée l'adresse de la mémoire sur le tas

char name[256]; // declare an array on the stack
name++; // error name is a constant pointer
*(name+2); // access 3rd item in array
name[2]; // access 3rd item in array
char *p = name;
p[2]; // access 3rd item in array
*(p+2); // access 3rd item in array
p++; // move p to item 1
p[0]; // item 1 in array

Traduction: le nom est un pointeur constant sur un caractère qui pointe sur de la mémoire sur la pile

En C, les tableaux et les pointeurs sont plus ou moins la même chose. Les tableaux sont des pointeurs constants vers la mémoire. La principale différence est que lorsque vous appelez malloc, vous extrayez votre mémoire du tas et toute mémoire extraite du tas doit être libérée du tas. Lorsque vous déclarez le tableau avec une taille, la pile lui attribue de la mémoire. Vous ne pouvez pas libérer cette mémoire car free est conçu pour libérer de la mémoire du tas. La mémoire de la pile sera automatiquement libérée lors du retour du programme actuel. Dans le deuxième exemple, free (p) serait également une erreur. p est un pointeur le tableau de noms sur la pile. Donc, en libérant p, vous essayez de libérer de la mémoire sur la pile.

Ce n'est pas différent de:

int n = 10;
int *p = &n;

libérer p dans ce cas serait une erreur car p pointe sur n qui est une variable de la pile. Par conséquent, p contient un emplacement de mémoire dans la pile et ne peut pas être libéré. ??

int *p = (int *) malloc(sizeof(int));
*p = 10;
free(p);

dans ce cas, free est correct car p pointe sur un emplacement mémoire du tas alloué par malloc.

En fonction du lieu où vous exécutez cette opération, l’espace de pile peut être extrêmement coûteux. Si, par exemple, vous écrivez du code BREW pour les combinés Verizon / Alltel, vous êtes généralement limité aux piles minuscules, mais vous disposez d’un nombre croissant d’accès au segment de mémoire.

De plus, étant donné que les caractères char [] sont le plus souvent utilisés pour les chaînes, il n’est pas mauvais d’autoriser la méthode de construction de chaîne à allouer la mémoire dont elle a besoin pour la chaîne en question, plutôt que d’espérer que pour toujours et toujours 256 (ou quel que soit le nombre que vous décidez) suffira.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top