103 votes

C La Gestion De La Mémoire

J'ai toujours entendu dire qu'en C, vous devez vraiment regarder dans la façon de gérer la mémoire. Et je suis encore commencé à apprendre le C, mais jusqu'à présent, je n'ai pas eu à faire de la mémoire de la gestion des activités liées à tous.. j'ai toujours imaginé avoir à libérer les variables et de faire toutes sortes de choses laides. Mais cela ne semble pas être le cas.

Quelqu'un peut-il me montrer (avec des exemples de code) un exemple de cas où vous auriez à faire un peu de "gestion de la mémoire" ?

246voto

Euro Micelli Points 12845

Il y a deux endroits où les variables peuvent être mis en mémoire. Lorsque vous créez une variable comme ceci:

int  a;
char c;
char d[16];

Les variables sont créées dans la "pile". Pile les variables sont automatiquement libérés quand ils sortent de la portée (c'est à dire lorsque le code ne peut pas les atteindre plus). Vous entendrez peut-être appelé "automatique" des variables, mais qui est tombé de la mode.

Beaucoup de débutant exemples utilisent uniquement des variables de pile.

La pile est sympa parce que c'est automatique, mais il a aussi deux inconvénients: (1) Le compilateur a besoin de savoir à l'avance comment les grandes variables, et (b) l'espace de pile est quelque peu limitée. Par exemple: dans Windows, sous paramètres par défaut de l'éditeur de liens Microsoft, la pile est de 1 MO, et pas tous, il est disponible pour les variables.

Si vous ne savez pas au moment de la compilation la taille de votre tableau, ou si vous avez besoin d'un grand tableau ou structure, vous avez besoin de "plan B".

Le Plan B est appelé le "tas". Habituellement, vous pouvez créer des variables aussi grande que le Système d'Exploitation permettra de vous, mais vous devez le faire vous-même. Plus tôt offres vous a montré d'une façon que vous pouvez le faire, bien qu'il existe d'autres moyens:

int size;
// ...
// Set size to some value, based on information available at run-time. Then:
// ...
char *p = (char *)malloc(size);

(Notez que les variables dans le tas ne sont pas manipulés directement, mais par l'intermédiaire de pointeurs)

Une fois que vous créez un tas de variable, le problème est que le compilateur ne peut pas dire quand vous avez fini avec elle, de sorte que vous perdez de l'automatique, de la relâcher. C'est là que le "manuel de libérer" vous faisiez référence à vient en. Votre code est maintenant responsable pour décider quand la variable n'est plus nécessaire, et libérer ainsi de la mémoire peut être pris à d'autres fins. Pour le cas ci-dessus, avec:

free(p);

Ce qui rend cette deuxième option "sale affaire", c'est qu'il n'est pas toujours facile de savoir quand la variable n'est plus nécessaire. Oublier de libérer une variable lorsque vous n'en avez pas besoin, il sera la cause de votre programme à consommer plus de mémoire qu'il doit. Cette situation est appelée une "fuite". La "fuite" de la mémoire ne peut pas être utilisé pour quoi que ce soit jusqu'à la fin de votre programme et de l'OS récupère la totalité de ses ressources. Même les plus méchants problèmes sont possibles si vous relâchez un tas de variable par erreur avant de vous sont effectivement fait avec elle.

En C et C++, vous êtes responsable pour le nettoyage de votre tas de variables comme indiqué ci-dessus. Cependant, il y a des langues et environnements, tels que Java et .NET langages tels que C# qui utilisent une approche différente, où le tas devient nettoyé sur son propre. Cette seconde méthode, appelée "garbage collection", est beaucoup plus facile sur le développeur, mais vous devez payer une pénalité dans les frais généraux et de la performance. C'est un équilibre.

(J'ai passe beaucoup de détails à donner une plus simple, mais je l'espère plus nivelé réponse)

23voto

Mark Harrison Points 77152

Voici un exemple. Supposons que vous avez un strdup() fonction que les doublons d'une chaîne de caractères:

char *strdup(char *src)
{
    char * dest;
    dest = malloc(strlen(src) + 1);
    if (dest == NULL)
        abort();
    strcpy(dest, src);
    return dest;
}

Et vous l'appeler comme ceci:

main()
{
    char *s;
    s = strdup("hello");
    printf("%s\n", s);
    s = strdup("world");
    printf("%s\n", s);
}

Vous pouvez voir que le programme fonctionne, mais vous avez la mémoire allouée (par malloc) sans libérer. Vous avez perdu votre pointeur sur le premier bloc de mémoire lorsque vous avez appelé strdup la deuxième fois.

Ce n'est pas une grosse affaire pour cette petite quantité de mémoire, mais de considérer le cas:

for (i = 0; i < 1000000000; ++i)  /* billion times */
    s = strdup("hello world");    /* 11 bytes */

Vous avez maintenant utilisé en hausse de 11 go de mémoire (peut-être plus, en fonction de votre gestionnaire de mémoire) et si vous n'avez pas écrasé votre processus est probablement en cours d'exécution assez lentement.

Pour le fixer, vous avez besoin d'appeler free() pour tout ce qui est obtenu avec la fonction malloc() après que vous avez fini de l'utiliser:

s = strdup("hello");
free(s);  /* now not leaking memory! */
s = strdup("world");
...

Espérons que cet exemple permet!

10voto

Jeremy Ruten Points 59989

Vous avez à faire "gestion de la mémoire" lorsque vous souhaitez utiliser la mémoire sur le tas plutôt que de la pile. Si vous ne savez pas quelle taille pour faire un tableau, jusqu'à l'exécution, alors vous devez utiliser le tas. Par exemple, vous pourriez vouloir stocker quelque chose dans une chaîne de caractères, mais vous ne savez pas quelle taille de son contenu sera jusqu'à ce que le programme est exécuté. Dans ce cas, vous devez écrire quelque chose comme ceci:

 char *string = malloc(stringlength); // stringlength is the number of bytes to allocate

 // Do something with the string...

 free(string); // Free the allocated memory

9voto

Bill Forster Points 3298

Je pense que le plus concis façon de répondre à la question à examiner le rôle du pointeur dans C. Le pointeur est un poids léger mais puissant mécanisme qui vous donne un immense liberté au prix d'une immense capacité à se tirer dans le pied.

En C, la responsabilité de s'assurer que votre pointeurs point de mémoire que vous possédez est à vous et à vous seul. Cela nécessite une bonne organisation et de la discipline de l'approche, à moins d'abandonner les pointeurs, ce qui le rend difficile à écrire efficace C.

Les réponses à ce jour se concentrer sur automatique (pile), et des tas attribution de variables. À l'aide de l'allocation de pile ne prendre pour géré automatiquement et pratique de la mémoire, mais dans certaines circonstances (tampons de grande taille, d'algorithmes récursifs) elle peut conduire à la terrible problème de débordement de la pile. Savoir exactement la quantité de mémoire que vous pouvez allouer sur la pile est très dépendante sur le système. Dans certains des scénarios intégrés quelques dizaines d'octets peut être votre limite, dans certains scénarios de bureau que vous pouvez utiliser en toute sécurité mégaoctets.

Allocation de tas est moins inhérente à la langue. Il est essentiellement un ensemble d'appels de bibliothèque que vous accorde la propriété d'un bloc de mémoire de taille donnée jusqu'à ce que vous êtes prêt à retourner ("libre"). Cela paraît simple, mais elle est associée à d'indicibles programmeur chagrin. Les problèmes sont simples (libération de la mémoire même deux fois, ou pas du tout [fuites de mémoire], de ne pas allouer suffisamment de mémoire [débordement de la mémoire tampon], etc), mais difficile d'éviter et de débogage. Un hautement disciplinée approche est absolument obligatoire dans la pratique en la matière, mais bien sûr, la langue n'a pas de mandat.

Je voudrais mentionner un autre type d'allocation de mémoire qui a été ignoré par les autres postes. Il est possible d'allouer de manière statique des variables en les déclarant en dehors de toute fonction. Je pense que, en général, ce type d'allocation obtient une mauvaise réputation car il est utilisé par des variables globales. Cependant il n'y a rien qui dit que le seul moyen d'utiliser la mémoire allouée de cette façon est un indiscipliné variable globale dans un désordre de code spaghetti. L'allocation statique méthode peut être utilisée simplement pour éviter certains des pièges de la tas et de l'allocation automatique des méthodes. Certains programmeurs C sont surpris d'apprendre que les grandes et sophistiquée, C embarqué et des jeux, des programmes ont été construits à la non-utilisation des tas de répartition à tous.

6voto

Chris B-C Points 1031

Il y a quelques grandes réponses ici sur la façon d'allouer et de libérer de la mémoire, et à mon avis le plus difficile du côté de l'aide de C est de s'assurer que la mémoire que vous utilisez est de la mémoire que vous avez alloué - si cela n'est pas fait correctement ce que vous finissez avec est le cousin de ce site - un débordement de buffer - et vous pouvez effacer de la mémoire qui est utilisé par une autre application, avec des résultats aléatoires.

Un exemple:

int main() {
    char* myString = (char*)malloc(5*sizeof(char));
    myString = "abcd";
}

À ce stade, vous avez alloué 5 octets pour myString et rempli avec de l' "abcd\0" (chaînes de fin dans un null - \0). Si votre chaîne de l'allocation a été

myString = "abcde";

Vous seriez en attribuant des "abcde" dans les 5 octets que vous avez eu attribuée à votre programme, et le caractère null de fin de mise à la fin de celle - ci, une partie de la mémoire qui n'a pas été alloué à votre utilisation et à être libre, mais pourrait également être utilisé par une autre application - C'est la partie critique de la gestion de la mémoire, où une erreur sera imprévisible (et parfois unique) les conséquences.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X