FAQ CConsultez toutes les FAQ

Nombre d'auteurs : 28, nombre de questions : 175, création le 11 janvier 2013 

 
OuvrirSommaireDiversBonnes pratiques

Contrairement aux idées reçues, le cast du malloc / calloc / realloc n'est pas obligatoire en C.

Dans les premières versions du C, le pointeur générique (void *) n'existait pas. La fonction malloc renvoyait un pointeur de type char *, ce qui rendait le cast obligatoire lorsque le pointeur utilisé pour stocker cette valeur retournée est de type différent.

Le C ANSI a cependant introduit le type "pointeur générique" (void *) qui, au vu des conversions implicites, peut être assigné, sans cast, à un pointeur de n'importe quel type (de même, un pointeur de n'importe quel type peut être assigné, sans cast, à un pointeur générique). Depuis, il n'était donc plus nécessaire de caster malloc.

Si votre compilateur refuse de compiler votre code tant que vous ne castez pas malloc, alors :

  • soit vous utilisez un compilateur non conforme (ce qui est très peu probable de nos jours à moins que vous utilisiez un compilateur datant de l'avant-normalisation, c'est-à-dire un compilateur des années 70 ou 80 ...).

  • soit vous compilez en mode "C++", c'est-à-dire que votre compilateur (qui est donc apparemment capable de compiler aussi bien du code C que du code C++) pense que votre code est écrit en C++ (dans lequel le cast de void * vers un autre type pointeur est obligatoire) et non en C (dans lequel caster malloc est non seulement inutile mais aussi de mauvais style). Généralement les fichiers sources C++ portent l'extension .cpp et les fichiers sources C l'extension .c, mais cela peut changer d'un compilateur à un autre. Il faut donc faire bien attention sur ce point là.
Mis à jour le 2009-01-03  par gl, Pouet_forever

La fonction scanf() est une fonction destinée, comme le f dans son nom l'indique, aux saisies formatées et s'avère ainsi très peu adaptée aux saisies humaines qui peuvent présenter des erreurs de format, ces erreurs de format pouvant entraîner des débordements de tampon, des boucles infinies, des perturbations sur les saisies suivantes, etc. Il est donc conseillé de remplacer les saisies formatées effectuées avec scanf() par une lecture complète via fgets() suivie d'une analyse de la chaîne lue.

Voici par exemple un programme qui entre dans une boucle infinie si jamais l'utilisateur tape quelque chose qui commence par un caractère invalide en début d'un nombre (une lettre par exemple) :

 
Sélectionnez
#include <stdio.h>
 
int main(void)
{
    int n = 0;
 
    while (n != 1)
    {
        printf("Tapez 1 pour quitter.\n");
        scanf("%d", &n);
    }
 
    return 0;
}

En effet, le spécificateur de format %d indique que scanf doit effectuer la lecture des caractères à partir de la position courante dans le tampon de lecture de l'entrée standard avec les règles suivantes :

 
Sélectionnez
Si des caractères sont présents
    1. Passer tous les caractères blancs (espaces, tabulations, fins de ligne).
    2. Lire les caractères présents jusqu-à la rencontre du premier caractère blanc ou
	   invalide dans l-expression d-un nombre entier. Le caractère qui a arrêté la lecture
	   n-est pas lu par scanf. Il sera donc le premier caractère rencontré dans la
	   prochaine lecture sur l-entrée standard.
    3. Convertir la chaîne lue en entier et stocker le résultat à l-adresse spécifiée.
Sinon
    - Attendre que l-utilisateur tape une ligne.
    - Appliquer 1, 2 puis 3.
Fin Si

Ainsi, en tapant "ab12" par exemple, 'a' étant un caractère invalide dans l'expression d'un entier, le curseur de scanf n'avancera jamais (il restera éternellement bloqué face à ce caractère !).

L'utilisation de fgets permet d'éliminer très simplement ce problème.

 
Sélectionnez
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
void clean_stdin(void); /* Voir '[FAQ] Comment vider le buffer du clavier ?'. */
 
int main(void)
{
    int ok = 0; /* 0 = false */
 
    while (!ok)
    {
        char buffer[256], *p;
        int n;
 
        printf("Tapez 1 pour quitter.\n");
 
        /* Lecture de la chaine tapee. */
 
        fgets(buffer, sizeof(buffer), stdin);
 
        /* Supprimer le '\n' de la chaine lue s'il est present. */
 
        if ((p = strchr(buffer, '\n')) != NULL)
            *p = '\0';
        else
        {
            /* Si le '\n' est absent, vider le buffer du clavier. */
 
            clean_stdin();
        }
 
        /* Analyse de la chaine lue. */
 
        n = strtol(buffer, &p, 10); /* strtol permet de passer de "123" (buffer) a 123 (n). */
 
        if (*p == '\0')
        {
            /* On peut utiliser n */
 
            if (n == 1)
                ok = 1;
        }
    }
 
    return 0;
}
Mis à jour le 2009-03-23  par gl, Emmanuel Delahaye

Lien : Comment vider le buffer clavier ?

La fonction gets() ne permet pas de spécifier la taille du tampon destiné à contenir la chaîne lue et expose donc le programme aux attaques par débordement de tampon. Cette fonction ne doit donc jamais être utilisée dans un programme digne d'être respecté.

Mis à jour le 2009-03-23  par gl, Emmanuel Delahaye

Lien : Comment lire une ligne de manière securisée ?

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée.