char x vs. char * = malloc (256 * sizeof (char));

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?

Dans le premier code, la mémoire est allouée dynamicment 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 second code, les 256 octets sont alloués sur la stack 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 fuir, mais il ne vivra pas au-delà de la fin de la fonction.

Choisissez entre les deux en fonction des exigences de la mémoire.

Addendum (Pax):

Si je peux append quelque chose à cela, Ned, la plupart des implémentations fourniront généralement plus de tas que de stacks (au moins par défaut). Cela ne compte généralement pas pour 256 octets, sauf si vous êtes déjà à court de stack ou si vous faites des tâches très récursives.

De plus, sizeof (char) est toujours égal à 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.

End addenda (Pax).

et que les deux sens nécessiteraient l’utilisation de free ().

Non, seul le premier nécessite l’utilisation d’un logiciel gratuit. La seconde est allouée sur la stack. Cela 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 stack. La stack est une grande partie de la mémoire continue située quelque part. Mettre quelque chose sur la stack incrémentera (ou diminuera selon votre plate-forme) le pointeur de stack. Sortir de la scope 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 endroit 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 appeler gratuitement en passant l’adresse que vous avez obtenue manuellement de malloc si le moment est venu de le faire.

Une forme de C “récente”, appelée C99, vous permet de donner aux tableaux une taille d’exécution. C’est-à-dire que 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 dans les réponses à une autre question sur SO que quelqu’un pensait que c’était inapproprié en C; voici ce n’est même pas mentionné, et je suis un peu confus et surpris (comme “qu’est-ce qu’ils enseignent 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 stack, mais dans l’espace de données de l’image. Ainsi, il ne faut ni gérer comme avec malloc / free, ni vous préoccuper de la réutilisation de l’adresse comme vous le feriez avec une définition automatique.

Il est utile de rappeler ici tout ce qui est “déclaré” ou “défini”. 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 fichier complètement différent

 /* 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 stack (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: nom est maintenant un pointeur sur un caractère auquel est atsortingbué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: nom est un pointeur constant sur un caractère qui pointe sur de la mémoire sur la stack

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 stack lui atsortingbue 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 stack 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 stack. Donc, en libérant p, vous essayez de libérer de la mémoire sur la stack.

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 stack. Par conséquent, p contient un emplacement de mémoire dans la stack 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 sur le tas qui a été alloué par malloc.

En fonction de l’endroit où vous exécutez cette opération, l’espace de stack peut constituer une énorme prime. Si, par exemple, vous écrivez du code BREW pour les combinés Verizon / Alltel, vous êtes généralement limité aux stacks minuscules, mais vous disposez d’un nombre croissant d’access.

De plus, étant donné que char [] est le plus souvent utilisé 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 le nombre décret) suffira.