Partage
  • Partager sur Facebook
  • Partager sur Twitter

Utilisation des pointeurs en C++

    26 juin 2018 à 10:24:33

    Ce que je veux dire c'est que ta solution est bonne seulement si la documentation de la fonction dit explicitement

           int MyStrLen(char *ptr_str);
    
           La fonction MyStrLen calcule la longueur d'une 
           chaine pointée par ptr_str, en excluant le caractère
           nul de fin.
    
    --->   Elle retourne -1 dans le cas où ptr_str contient
           le pointeur NULL.
    

    Si non, le programmeur qui emploie ta fonction ne sait pas quoi faire du résultat.  Remarque que de toutes façons, qu'il teste avant l'appel que le pointeur n'est pas nul, ou après que la longueur n'est pas -1, ça ne lui absolument pas simplifié la vie. Faut tester de toute façon. Ca partait d'un bon sentiment, mais en fait ça bouffe des cycles pour rien.

    Donc faut y aller mollo avec les superstitions du genre "la fonction reçoit des pointeurs alors que je vérifie toujours qu'ils sont pas NULL". Et si on reçoit des pointeurs non initialisés qui vont dans la nature, on fait quoi ?

    En plus c'est une source d'emmerdements. Une longueur, c'est positif ou nul, donc le résultat devrait être un entier non signé. L'emploi de valeurs sentinelles "out of band", ça oblige à relacher les contraintes de types que le compilateur, qui est notre ami, pourrait faire (par exemple, pour comparer des longueurs, c'est sympa que les deux soient non-signées).

    Remarques

    •  ta fonction IsNullChar est horriblement mal nommée. Elle teste si un pointeur est nul, pas un caractère.
    • définir une fonction pour tester si un pointeur est nul, est-ce bien utile ?
    •   le for(...); est un piège à cons notoire, le point-virgule étant peu visible. Préférence perso pour un bloc vide  for(...) {}  qui donne clairement à voir qu'il y a bien un corps de boucle vide, et que ce n'est pas un point virgule arrivé là par accident. Et ça ne coûte qu'un caractère de plus.
    • le travail sur les chaines de caractères est l'occasion de faire les fantaisies cryptiques à coup de pré/post incrémentations qui faisaient le charme du C à l'ancienne
    const char *p = ptr_str;
    while (*p++) {}
    return p - ptr_str;
    

    remplacer la condition par *p++ == '\0'  pour une version plus explicite

    -
    Edité par michelbillaud 26 juin 2018 à 10:37:15

    • Partager sur Facebook
    • Partager sur Twitter
      26 juin 2018 à 12:06:07

      Ksass`Peuk a écrit:

      Si tu reçois une chaîne invalide dans strlen c'est une rupture du contrat, donc c'est pas un scénario d'erreur du programme, c'est un scénario d'erreur du développeur.

      C'est à dire ? Comment pourrions-nous améliorer MyStrlen dans ce cas ?

      michelbillaud a écrit:

      Ce que je veux dire c'est que ta solution est bonne seulement si la documentation de la fonction dit explicitement

      Ca va de sois ;)

      michelbillaud a écrit:

      [Pour le reste...]

      OK, je prend note :)

      J'imagine aussi que les exceptions du C++ peuvent dans ces cas parfois s'avérées utiles, non ?

      Merci pour tout.

      -
      Edité par Geralt de Riv 26 juin 2018 à 12:10:25

      • Partager sur Facebook
      • Partager sur Twitter
      Le doute est le commencement de la sagesse
        26 juin 2018 à 12:30:19

        Geralt de Riv a écrit:

        Ksass`Peuk a écrit:

        Si tu reçois une chaîne invalide dans strlen c'est une rupture du contrat, donc c'est pas un scénario d'erreur du programme, c'est un scénario d'erreur du développeur.

        C'est à dire ? Comment pourrions-nous améliorer MyStrlen dans ce cas ?


        J'imagine aussi que les exceptions du C++ peuvent dans ces cas parfois s'avérées utiles, non ?

        1) Amélioration : ne pas s'occuper du pointeur nul dans MyStrLen, si ce n'est pas spécifié qu'elle doit s'en occuper. C'est au programmeur qui _emploie_ la fonction de vérifier qu'il donne bien des chaines, et pas un pointeur nul. C'est au conducteur de vérifier qu'il ne met pas de diesel dans le réservoir d'une voiture à essence. Pas le boulot de la voiture.


        2) Les exceptions sont effectivement un bon moyen de signaler des erreurs.

        • Partager sur Facebook
        • Partager sur Twitter
          26 juin 2018 à 12:31:55

          Parfais, j'ai tout compris :)

          Merci encore !

          • Partager sur Facebook
          • Partager sur Twitter
          Le doute est le commencement de la sagesse
            26 juin 2018 à 16:58:22

            Salut,

            Ce que tu peux faire, c'est placer une assertion dans myStrlen : si on veut avoir un résultat cohérent (ca, c'est une partie du contrat) la contrepartie est de fournir un pointeur sur une chaine valide.

            Si le pointeur fourni ne pointe pas sur une chaine valide, il y a une rupture du contrat (occasionnée par une erreur de logique de la part de l'tutilisateur)

            L'utilisateur (de la fonction) doit donc corriger la logique qui mène à l'appel de la fonction avant de faire quoi que ce soit d'autre. Mais, du coup, la vérification de ce point n'a -- en théorie -- absolument aucune raison de subsister dans le code qui sera "livré" pour partir en production, vu que l'utilisateur (de la fonction)  est sensé avoir corrigé toutes ses erreurs de logiques.

            C'est -- en gros -- ce que fait la macro assert: en débug / développement, elle fera planter l'application au motif que

            assertion failed in file <machin> at line <XYZ> :
            (condition qui n'est pas respectée)

            Et, comme on ne pourra pas aller plus loin (dans les différents tests) tant que la condition indiquée ne sera pas respectée quel que soit le chemin utilisé pour y arriver, on peut effectivement partir du principe que, lorsque l'on arrivera à "aller plus loin", c'est que le développeur aura corrigé tous les endroits où sa logique était foireuse.

            par contre, lorsque l'on décidera de passer en relase / production, la macro assert sera transformée en "no op"

            Pour voir un exemple cohérent, compile le code suivant en mode debug et en mode release, et exécute le.

            #include <cassert>
            
            
            void foo(int * ptr){
                /* on s'attend à ce que ptr ne soit pas null
                asert(ptr!= nullptr && "nullpointer detected");
                std::cout<<"ptr vaut "<<*ptr<<"dans foo\n";
            }
            int main(){
                foo(nullptr);
            }

              Tu verras tout de suite la différence ;)

            • Partager sur Facebook
            • Partager sur Twitter
            Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
              26 juin 2018 à 17:57:48

              C'est vrai que je n'avais pas pensé à assert. Et à chaque fois que je l'utilisais, j'avais toujours une erreur. Je comprend mieux d'où elle vient maintenant :)

              Merci pour l'exemple et les explications :D

              • Partager sur Facebook
              • Partager sur Twitter
              Le doute est le commencement de la sagesse
                28 juin 2018 à 11:31:51

                Assertions et exceptions n'ont pas le même usage. On emploiera une assertion, comme l'ont dit mes petits camarades, pour détecter une erreur dans le programme (par exemple passer un pointeur nul à une fonction qui attend un pointeur valide).

                Les exceptions seront utilisées pour gérer des erreurs d'exécution qui ne sont pas des erreurs de programmation, par exemple le programme doit aller lire des données dans un fichier qui "n'existe pas":

                • Le fichier n'existe vraiment pas
                • Le compte dans lequel tourne le programme ne dispose pas des droits lui permettant d'accéder au fichier
                • Le fichier se trouve sur un lecteur réseau et le réseau est en panne
                • ...

                Ici nous sommes dans des cas d'erreurs qui ne sont pas des erreurs de programmation, le programmeur n'a commis aucune erreur, simplement l'environnement dans lequel le programme tourne n'est pas conforme à ce qui était prévu. Par exemple imaginons que mon programme transfère des données sur le réseau, il est tout à fait possible qu'un ouvrier de la voirie donne un malencontreux coup de pelleteuse sur le câble réseau par lequel transitent les données de mon programme, du coup le transfert de données va se passer moins bien. C'est pour traiter ce genre d'erreur que l'on va employer des exceptions.

                • Partager sur Facebook
                • Partager sur Twitter
                Mettre à jour le MinGW Gcc sur Code::Blocks. Du code qui n'existe pas ne contient pas de bug

                Utilisation des pointeurs en C++

                × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                • Editeur
                • Markdown