Developpez.com

Le Club des Développeurs et IT Pro

Mise à jour de la FAQ C :

Avec désormais 181 réponses à vos questions, pour mieux apprendre le langage C

Le 2017-02-19 02:26:35, par Malick, Community Manager
Chers membres du club,

j'ai l'immense plaisir de vous annoncer la mise à jour de la FAQ C avec une sélection des cent quatre-vingt-une meilleures réponses à vos questions pour apprendre la programmation avec ce langage.

Les nouveaux Q/R portent essentiellement sur les nouveautés de C11 par rapport à C99.



Le chargement des données d'un fichier texte dans une structure



Ce nombre est appelé à évoluer avec vos différentes contributions.

Nous remercions LittleWhite, Matt_Houston et Winjerome pour leur engagement dans la rédaction et la relecture technique. Nos reconnaissances également à Claude LELOUP pour les corrections orthographiques.

Vous pouvez aussi contacter l'équipe de rédaction
.
  Discussion forum
13 commentaires
  • sambia39
    Membre expérimenté
    Bonsoir
    Dans la section FAQ C Comment charger les données d'un fichier texte dans une structure ? pourquoi utiliser un assert ?
    Enfin (rien ne vous oblige), mais personnellement, je trouve qu’il n’est pas correct d’utiliser assert dans le cas présent surtout si l’objectif du FAQ est de fournir une réponse correspondant la questions récurrentes posées.

    Je suis d’avis à connaître la raison d’un échec d’appel d’une fonction, mais de là a utilisé une fonction qui a pour rôle ou un des rôles la détection d’erreur de programmation lors de débogage et interrompre le programme en renvoyant une terminaison anormale du programme en question; est excessive.

    Je pense qu’il est préférable d’utiliser un retour d’erreur que assert. Je m’explique : le retour d’erreur met en évidence une mauvaise utilisation de la fonction à cause d’un paramètre erroné (Exemple à cause d’une ouverture de fichier qui a échoué, ou autres, etc..), ce qui permet alors de prendre les mesures nécessaires pour traiter l’erreur et terminer le programme proprement.
    "assert" est utilisé (dans mon cas) quand on se rend compte de l’instabilité du programme ou lorsque l’on fait du débogage en clair "assert" doit être utilisé que pour des circonstances ne devant jamais ce produire durant l’exécution normale d’un programme c’est un outil de débogage et non une méthode, routine ou fonction de sortie d’erreur de plus la gestion d’erreur doivent impérativement être faites uniquement par le programme Dans l’exemple fournir il ne s’agit strictement pas d’un bogue..
    Admettant que l’on compile avec -DNDEBUG pensez-vous que le programme final soit correct (et portable) ?.

    Ceci dit la mise à jour de la FAQ était essentielle
    à bientôt
  • picodev
    Membre émérite
    Bonjour,
    concernant les «implémentations» complètes de C11 il faut citer les couples compilateurs/bibliothèque standard.
    Le couple gcc/musl est une implémentation de C11, au même titre que gcc/glibc (aux threads près si je ne me trompe pas).

    En tout cas bravo pour la mise à jour qui était nécessaire
  • Matt_Houston
    Expert confirmé
    Effectivement l'assert ne devrait pas être utilisé pour traiter un échec de fopen, bien vu sambia.

    Je ne suis pas totalement d'accord - c'est mineur, mais la FAQ C est précisément l'endroit pour chipoter ! - avec la solution de remplacement proposée par picodev :

    Envoyé par picodev
    Code :
    1
    2
    3
    4
    if (f==NULL) {
      perror("fopen");
      exit(EXIT_FAILURE);
    }
    Ce qui me chiffonne c'est l'usage d'exit : il n'y a pas de raison de « paniquer » ici, de plus nous sommes dans main où l'on peut lui préférer un simple et élégant return. On voit déjà cet appel utilisé à tort et à travers dans les programmes de débutants, autant ne pas encourager cette pratique.

    Envoyé par picodev
    Comme on est en C11 on peut aussi utiliser les designated init :

    au lieu de :
    Code :
    t_capteur c = { 0, "", 0.0f };
    on pourra préférer
    Code :
    t_capteur c = { .id=0, .nom="", .valeur=0.0f };
    Ça c'est plus une préférence personnelle mais je ne suis pas non plus d'avis d'user et abuser de cette syntaxe, certes souvent pratique mais inutilement grevante lorsqu'il n'y a pas d'ambiguïté comme ici.
  • picodev
    Membre émérite
    Envoyé par Bktero
    L'objectif de cette entrée n'est pas d'expliquer comment bien ouvrir un fichier mais de comment charger des données. Une assertion correspond bien à ce que ce bout de code : il doit s'assurer que le descripteur est valide. J'aurais pu mettre le reste du code d'un if(!NULL). Il n'y a pas de contexte au chargement des données, c'est vraiment un snippet à adapter, gestion d'erreur y compris.
    Utiliser assert pour vérifier une erreur runtime est une mauvaise pratique, elle est même fautive car si la macro NDEBUG est définie alors assert ne fait rien.
    Une assertion ne doit servir que lors du développement pour s'assurer qu'on utilise une API correctement par exemple, jamais pour s'assurer une allocation de ressources.
    J'imagine que l'objectif est aussi de ne pas donner de mauvaises habitudes.
    Envoyé par Bktero
    Il n'est pas précisé qu'on est en C11 dans cette entrée donc pas de designated initializers.
    Et du coup on passera en C99 quand la norme C2x sortira ?
  • picodev
    Membre émérite
    @sambia39: tout à fait. assert = à utiliser uniquement lors du développement pour traquer les erreurs d'utilisations de l'API par exemple, à ne jamais utiliser en version release pour traquer les erreurs runtime.
    Un exemple plus correct remplacera l'assertion
    Code :
    assert(f != NULL);
    par un
    Code :
    1
    2
    3
    4
    if (f==NULL) {
      perror("fopen");
      exit(EXIT_FAILURE);
    }

    Comme on est en C11 on peut aussi utiliser les designated init :

    au lieu de :
    Code :
    t_capteur c = { 0, "", 0.0f };
    on pourra préférer
    Code :
    t_capteur c = { .id=0, .nom="", .valeur=0.0f };
    Dans compte = fscanf(f, "%d-%[^:s]:%f\n", &(c.id), c.nom, &(c.valeur)); le parenthésage n'est pas nécessaire la priorité de l'opérateur . étant supérieure à celle de l'opérateur & unaire. Mais bon ce n'est qu'un détail.
  • picodev
    Membre émérite
    Envoyé par Matt_Houston
    Ce qui me chiffonne c'est l'usage d'exit : il n'y a pas de raison de « paniquer » ici, de plus nous sommes dans main où l'on peut lui préférer un simple et élégant return. On voit déjà cet appel utilisé à tort et à travers dans les programmes de débutants, autant ne pas encourager cette pratique.
    ...
    Oui c'est vrai ... un if else c'est mieux. L'important étant sans doute l'utilisation de perror.


    Ça c'est plus une préférence personnelle mais je ne suis pas non plus d'avis d'user et abuser de cette syntaxe, certes souvent pratique mais inutilement grevante lorsqu'il n'y a pas d'ambiguïté comme ici.
    C'est effectivement surtout utile dans la phase de développement où la composition de structures peut être modifiée, cela permet entre autre d'avoir des warnings pour des structures non complètement initialisée, et d'être certain que «la bonne partie» sera initialisée. Cela permet d'augmenter la lisibilité aussi dans certains cas : un truc = {20,1000,0}; est moins clair qu'un truc = { .temperature=20, .pression=1000, .echantillon=0}; ; pas la peine de retourner voir la définition pour savoir quoi correspond à quoi.
    C'est vrai que c'est verbeux pour un programme de 15 lignes, mais dès qu'on doit chercher loin pour avoir la définition c'est mieux.
  • sambia39
    Membre expérimenté
    Envoyé par Matt_Houston

    Ce qui me chiffonne c'est l'usage d'exit : il n'y a pas de raison de « paniquer » ici, de plus nous sommes dans main où l'on peut lui préférer un simple et élégant return. On voit déjà cet appel utilisé à tort et à travers dans les programmes de débutants, autant ne pas encourager cette pratique.
    Disant que le choix entre return et exit est plutôt d’ordre préférentiel, mais je l’admets tout de même qu’on l’utilise à tort. Personnellement, j’utilise la fonction exit uniquement dans le cas où j’ai une erreur irrécupérable qui s’est produite où quand un return de l’information de cette erreur excède plus de 3 niveaux de profondeur de fonction.

    à bientôt
  • Bonjour,

    Je partage l'avis de picodev sur l'utilisation d'assert.
    Je constate en même temps un « s » en trop dans le format de fscanf :
    Code :
    compte = fscanf(f, "%d-%[^:s]:%f\n", &(c.id), c.nom, &(c.valeur));
    qui n'a rien à voir avec le format de chaîne %s, mais qui comme les deux points, s'arrête au premier « s » rencontré. Et je rajouterais volontiers la taille afin de se protéger contre les éventuelles chaînes trop longues occasionnant des dépassements.
  • Bktero
    Modérateur
    Je crois que j'ai la flemme de développer une réponse quant a l'assertion et encore plus sur le C2x.

    Effectivement pour le s, c'est une erreur !
  • picodev
    Membre émérite
    Envoyé par Bktero
    L'objectif de cette entrée n'est pas d'expliquer comment bien ouvrir un fichier mais de comment charger des données.
    Envoyé par Bktero
    Je crois que j'ai la flemme de développer une réponse quant a l'assertion et encore plus sur le C2x.

    Effectivement pour le s, c'est une erreur !
    Dans ce cas, autant faire comme en amour : vieux vaut ne rien faire que de ne le faire qu'à moitié.
    Je propose simplement la suppression de l'assertion.