• # foobar

    Posté par . En réponse au journal Gestion des erreurs d’allocation mémoire en C. Évalué à 6. Dernière modification le 27 octobre 2016 à 12:52.

    Je trouve que wrapper malloc() est déjà trop lourd, je préfère rester simple, surtout en C. Dans tout les programmes non interactif, j'exit() après avoir utilisé perror():

     if (!(foo = malloc(size))) {
     perror("malloc(foo)");
     exit(EXIT_FAILURE);
     }

    Notez qu'il est possible d'avoir un message d'erreur un peu plus personnalisé si vous le souhaitez :

     if (!(foo = malloc(size))) {
     fprintf(stderr, "malloc(%zu): %s\n", size, strerror(errno));
     exit(EXIT_FAILURE);
     }

    Mais peu importe les circonstances, intentionnellement provoquer une segfault est une très mauvaise pratique, car il est alors impossible de faire la différence entre un crash à cause de malloc() et un bug réel, d'autant qu'un crash et qu'un exit() ce n'est pas la même chose.

    Dans les programmes interactif ou les bibliothèques, j'échoue toujours gracieusement. A ce propos, voici une astuce assez connue à base de goto pour nettoyer les ressources allouées dans une même fonction (les messages d'erreur sont omis) :

    int fum(size_t size)
    {
     void *foo, *bar, *baz;
     if (!(foo = malloc(size)))
     goto err_foo;
     if (!(bar = malloc(size)))
     goto err_bar;
     if (!(baz = malloc(size)))
     goto err_baz;
     /* ... */
     return 1;
    err_baz:
     free(bar);
    err_bar:
     free(foo);
    err_foo:
     return 0;
    }

    Mais voici le point ou je voulais en venir : le plus possible j'essaye d'utiliser l'allocation « sur la pile »* ou statique. Cela veut dire mettre une borne haute à mes conteneurs. Combiné à sizeof() c'est une façon de faire qui est très plaisante, et très rapide de surcroît. L'utilisation de snprintf() est un vrai plaisir :

    struct player {
     char name[32];
     char clan[32];
    };
    static void set_default_player_name(
     struct player *player, const char *firstname, const char *lastname)
    {
     snprintf(player->name, sizeof(player->name), "%s.%s", firstname, lastname);
    }

    Un exemple pas très recommandable mais qui montre que sizeof() est très pratique :

    #define IPV6_STRSIZE sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx")
    

    Dans l'exemple qui suit, notez bien la ligne buf[] = "yyyy-mm-ddThh-mm-ssZ". Les dates au format RFC 3339 ont une taille fixe, c'est un des nombreux avantages de ce standard. Voici un excellent article sur la gestion des dates et des temps en C.

    /* RFC 3339 */
    static void print_zulu_time(struct tm *tm)
    {
     char buf[] = "yyyy-mm-ddThh-mm-ssZ";
     if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", tm))
     puts(buf);
    }

    Même pour ce qui ne relève pas des chaînes de caractère j'essaye de trouver des bornes haute raisonnable :

    #define MAX_PLAYERS 32
    #define MAX_PACKET_DATA 1400
    

    Au final, je me retrouve très rarement à utiliser malloc(). Uniquement dans les cas ou la taille est potentiellement trop grande pour allouer « sur la pile »* ou statique. Et lorsque je ne connaît pas à l'avance la taille de mon buffer, alors j'utilise souvent realloc()...


    *: Techniquement, le standard C n'a pas de notion de pile, lorsque l'on dit « variable allouée sur la pile », on fait référence aux variables avec une automatique storage duration. Enfin bon, ça ne change pas grand chose.