URL: https://linuxfr.org/users/tchetch/journaux/genese-d-un-journal Title: Genèse d'un journal Authors: Etienne Bagnoud Date: 2012年09月09日T11:32:13+02:00 License: CC By-SA Tags: c et realloc Score: 33 Mon [journal précédent parlait de realloc](https://linuxfr.org/users/tchetch/journaux/realloc) dont on ne contrôlait pas la valeur de retour. Suite à ce journal j'ai été très surpris par le nombre de commentaires clamant que ce n'était pas important, que le noyau se chargerait de tuer le processus, que le programme planterait, ... Je vais donc expliquer ce qui m'a amener à écrire ce journal. Je codais un petit truc vite fait en C et j'avais besoin d'utiliser `snprintf`. Cette fonction prend, en paramètre, la taille de la zone mémoire. Si cette zone mémoire ne suffit pas, elle retourne la taille qui aurait été nécessaire pour inscrire toute la chaîne de caractère. Il est difficile de savoir la taille à l'avance. Un simple `"%d"` peut nécessité de 1 à plus de X caractères. Il faut donc, dans certains cas, le faire en deux fois. On évalue la taille qui sera généralement suffisante et on prévoit le cas où ce n'est pas suffisant. Ça donne un code qui ressemble à ça : ```c #define DEFAULT_SIZE 30 int size=0; char * str=NULL; char * tmp=NULL; /* ... */ str=malloc(DEFAULT_SIZE * (sizeof * str)); if(str!=NULL) { size=snprintf(str, DEFAULT_SIZE, /* ... */); /* man page dit : "Thus, a return value of size or more means that the output * was truncated." */ if(size>=DEFAULT_SIZE) { /* snprintf retourne taille sans le '0円' */ size++; tmp=realloc(str, size * (sizeof * str)); if(tmp!=NULL) { size=tmp; snprintf(str, size, /* ... */); } else { free(str); str=NULL; } } } ``` En écrivant la ligne `tmp=realloc`, j'avais commencé par écrire `str=realloc`. Je me suis arrêté net et j'ai pesté. J'ai pesté parce que je sais que c'est une erreur généralement acceptée. L'idée du journal m'était venue, il me manquait plus qu'un exemple concret dans un programme existant et utilisé. Trouver un programme fut d'une simplicité enfantine. N'importe qu'elle programme qui semble manger beaucoup trop de mémoire est certainement bourré de ce genre d'erreur. J'avais remarqué conky il y'a bien longtemps. Avec un `VmPeak` de 281212 kB et un `VmData` de 82612 kB pour afficher, _en texte_, la date, la charge de la batterie, le nom du réseau Wifi et mon adresse IP, ça transpire le gaspillage. Un `git clone` et un `grep -R realloc *` plus tard, j'avais une quinzaine d'exemple de `realloc` foireux. Soit tous les realloc, sans exception. Les malloc sont aussi foireux. J'ai fait un patch pour ce que je pouvais, rapidement, corriger. `VmPeak`, sur la configuration par défaut, passe de 317916 kB à 211388 kB et `VmData` de 189372 kB à 123836 kB. Une amélioration notable après environ 4 ou 5 realloc corrigés. Comme a commenté "pasBill pasGates", à propos de gérer correctement les retours de fonctions comme realloc ou malloc :> [...] c'est une question d'hygiene de base de mon point de vue. Ne pas le faire, ce n'est pas seulement manquer d'hygiène, c'est surtout faire preuve de laxisme. Non, on ne peut pas compter sur le magicien "OOM Killer" de Linux. Le noyau a pu être compiler sans ça. Non, attendre un SEGFAULT pour quitter le programme n'est pas une solution. Et, surtout, ce n'est pas une optimisation que de gagner un ou deux cycles processeurs en enlevant un `if(x==NULL)`. "pasBill pasGates" a aussi dit :> Arreter le programme peut-etre oui, selon ce que le soft fait, mais il faut le faire proprement , pas avec un SEGFAULT ou un assert. C'est exactement le cas. Votre programme de traitement de texte n'a pas assez d'espace pour le prochain caractère donc il plante sans possibilité de sauvegarder ? Ce n'est pas une solution. Et l'excuse du programme non-critique n'en est pas une. Si un utilisateur décide d'utiliser votre programme, alors c'est un programme critique. Le C est un langage qui impose cette gestion de mémoire. Si vous ne vous sentez pas de la faire, alors le C n'est pas pour vous.