Je comprends le fonctionnement de malloc (). Ma question est, je vais voir des choses comme ça:
#define A_MEGABYTE (1024 * 1024)
char *some_memory;
size_t size_to_allocate = A_MEGABYTE;
some_memory = (char *)malloc(size_to_allocate);
sprintf(some_memory, "Hello World");
printf("%s\n", some_memory);
free(some_memory);
J'ai omis de vérifier les erreurs par souci de concision. Ma question est, ne pouvez-vous pas simplement faire ce qui précède en initialisant un pointeur vers un stockage statique en mémoire? peut-être:
char *some_memory = "Hello World";
À quel moment avez-vous réellement besoin d'allouer la mémoire vous-même au lieu de déclarer/initialiser les valeurs que vous devez conserver?
char *some_memory = "Hello World";
crée un pointeur sur une constante de chaîne. Cela signifie que la chaîne "Hello World" se trouvera quelque part dans la partie en lecture seule de la mémoire et vous n'aurez qu'un pointeur dessus. Vous pouvez utiliser la chaîne en lecture seule. Vous ne pouvez pas y apporter des modifications. Exemple:
some_memory[0] = 'h';
Demande des ennuis.
D'autre part
some_memory = (char *)malloc(size_to_allocate);
alloue un tableau de caractères (une variable) et certains points de mémoire à cette mémoire allouée. Maintenant, ce tableau est à la fois en lecture et en écriture. Vous pouvez désormais:
some_memory[0] = 'h';
et le contenu du tableau devient "bonjour le monde"
Pour cet exemple exact, malloc est de peu d'utilité.
La principale raison pour laquelle malloc est nécessaire est lorsque vous avez des données qui doivent avoir une durée de vie différente de la portée du code. Votre code appelle malloc dans une routine, stocke le pointeur quelque part et appelle éventuellement gratuitement dans une routine différente.
Une raison secondaire est que C n'a aucun moyen de savoir s'il reste suffisamment d'espace sur la pile pour une allocation. Si votre code doit être 100% robuste, il est plus sûr d'utiliser malloc car votre code peut alors connaître l'allocation a échoué et la gérer.
malloc est un merveilleux outil pour allouer, réallouer et libérer de la mémoire lors de l'exécution, par rapport aux déclarations statiques comme votre exemple hello world, qui sont traitées au moment de la compilation et ne peuvent donc pas être modifiées en taille.
Malloc est donc toujours utile lorsque vous traitez des données de taille arbitraire, comme lire le contenu d'un fichier ou traiter des sockets et que vous n'êtes pas conscient de la longueur des données à traiter.
Bien sûr, dans un exemple trivial comme celui que vous avez donné, malloc n'est pas le "bon outil magique pour le bon travail", mais pour les cas plus complexes (création d'un tableau de taille arbitraire au moment de l'exécution par exemple), c'est le seul moyen de aller.
Si vous ne connaissez pas la taille exacte de la mémoire que vous devez utiliser, vous avez besoin d'une allocation dynamique (malloc
). Un exemple peut être lorsqu'un utilisateur ouvre un fichier dans votre application. Vous devrez lire le contenu du fichier en mémoire, mais bien sûr, vous ne connaissez pas la taille du fichier à l'avance, car l'utilisateur sélectionne le fichier sur place, au moment de l'exécution. Donc, fondamentalement, vous avez besoin de malloc
lorsque vous ne connaissez pas la taille des données avec lesquelles vous travaillez à l'avance. C'est du moins l'une des principales raisons d'utiliser malloc
. Dans votre exemple avec une chaîne simple dont vous connaissez déjà la taille au moment de la compilation (en plus vous ne voulez pas la modifier), cela n'a pas beaucoup de sens de l'allouer dynamiquement.
Légèrement hors sujet, mais ... vous devez faire très attention à ne pas créer de fuites de mémoire lorsque vous utilisez malloc
. Considérez ce code:
int do_something() {
uint8_t* someMemory = (uint8_t*)malloc(1024);
// Do some stuff
if ( /* some error occured */ ) return -1;
// Do some other stuff
free(someMemory);
return result;
}
Voyez-vous ce qui ne va pas avec ce code? Il existe une instruction de retour conditionnel entre malloc
et free
. Cela peut sembler correct au début, mais pensez-y. S'il y a une erreur, vous allez revenir sans libérer la mémoire que vous avez allouée. Il s'agit d'une source courante de fuites de mémoire.
Bien sûr, ceci est un exemple très simple, et il est très facile de voir l'erreur ici, mais imaginez des centaines de lignes de code jonchées de pointeurs, malloc
s, free
s, et toutes sortes de gestion des erreurs . Les choses peuvent devenir vraiment désordonnées très rapidement. C'est l'une des raisons pour lesquelles je préfère de loin le C++ moderne au C dans les cas applicables, mais c'est un tout autre sujet.
Ainsi, chaque fois que vous utilisez malloc
, assurez-vous toujours que votre mémoire est aussi susceptible d'être free
d que possible.
char *some_memory = "Hello World";
sprintf(some_memory, "Goodbye...");
est illégal, les littéraux de chaîne sont const
.
Cela allouera un tableau de caractères de 12 octets sur la pile ou globalement (selon l'endroit où il est déclaré).
char some_memory[] = "Hello World";
Si vous souhaitez laisser de la place pour une manipulation supplémentaire, vous pouvez spécifier que le tableau doit être plus grand. (Veuillez ne pas mettre 1 Mo sur la pile, cependant.)
#define LINE_LEN 80
char some_memory[LINE_LEN] = "Hello World";
strcpy(some_memory, "Goodbye, sad world...");
printf("%s\n", some_memory);
L'une des raisons pour lesquelles il est nécessaire d'allouer la mémoire est si vous souhaitez la modifier au moment de l'exécution. Dans ce cas, un malloc ou un tampon sur la pile peut être utilisé. L'exemple simple d'affectation de "Hello World" à un pointeur définit une mémoire qui "généralement" ne peut pas être modifiée au moment de l'exécution.