IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

optimisation de ma fonction strlen


Sujet :

C

  1. #1
    Membre confirmé Avatar de zozoman
    Homme Profil pro
    Futur ex-prof
    Inscrit en
    Décembre 2007
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Allier (Auvergne)

    Informations professionnelles :
    Activité : Futur ex-prof
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 119
    Par défaut optimisation de ma fonction strlen
    Bonjour à tous,

    Je débute en C et j'ai bien du mal avec les pointeurs.

    J'ai créé une fonction qui trouve la longueur d'une chaine. Mon problème est que je passe à ma fonction un pointeur longueur initialisé dans la fonction main.
    Pourtant, la longueur de la chaine est indépendante de ce pointeur. Suis-je obligé de passer ce pointeur ?

    Ma fonction est elle optimisée ? J'ai en effet vu d'autre programmes beaucoup plus longs et plus complexes.


    Merci pour vos réponses

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
     
    #include <stdio.h>
    #include <stdlib.h>
     
    int longueurChaine(char*chaine,int*ptLongueur);
     
    int main()
    {
        char chaine[1000]={0};
        int longueur=0;
        int *ptLongueur=NULL;
        ptLongueur=&longueur;
        printf("Ce programme permet de calculer la longueur d'une chaine de caracteres\n\n");
        printf("Entrez une chaine de caracteres : ");
        gets(chaine);
        rewind(stdin);
        longueurChaine(chaine,ptLongueur);
        printf("\n\nLa longueur de la chaine \"%s\" est de %d caracteres\n\n",chaine,*ptLongueur);
        return 0;
    }
    int longueurChaine(char*chaine,int*ptLongueur)
    {
        int i=0;
        while (chaine[i]!='\0')
        {
            i++;
        }
        *ptLongueur=i;
        return *ptLongueur;
    }

  2. #2
    Membre chevronné

    Profil pro
    Inscrit en
    Août 2007
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 179
    Par défaut
    Pourquoi n'utilises tu pas le retour de la fonction?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    printf("\n\nLa longueur de la chaine \"%s\" est de %d caracteres\n\n",chaine,longueurChaine(chaine,ptLongueur));
    Du coup tu peux virer ton pointeur.

    rmq : c'est sûrement plus propre de passer pas une variable.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int res;
    (...)
    res = longueurChaine(chaine,ptLongueur);
    (...)
    printf("\n\nLa longueur de la chaine \"%s\" est de %d caracteres\n\n",chaine,res);
    Ou plutôt la même chose sans le second paramètre une fois que tu l'auras supprimé.

  3. #3
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Bonjour,

    Tout d'abord, bravo pour ta démarche.

    Voici quelques conseils puisque tu débutes:
    • Ne JAMAIS utiliser gets(), elle provoque un accès mémoire illégal (buffer overflow) dès que l'utilisateur saisie une chaine plus grande que le buffer alloué (ici char[1000])
      C'est un vrai danger, par exemple, de nombreux virus sont basés sur l'usage d'overflow connu.
    • Pour allouer un tableau, on préfère utiliser une constante
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      #define CHAINE_MAX 1000
      //...
      char[CHAINE_MAX] chaine;
      Si ton compilateur est récent, il devrait accepter la variante suivante, qui est largement préférable (contrôle du type, absence d'effet pervers des macros...):
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      int const CHAINE_MAX=1000;
      //...
      char[CHAINE_MAX] chaine;
      Le fait d'être constant est obligatoire en l'état actuel de la norme du C.
    • Il est rare d'utiliser un initialiseur qui n'est pas de la taille du buffer. j'aurais plutot mis
    • En effet, pourquoi utiliser un argument-retour (int* ptLongueur) si tu retourne cette même valeur...
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      longueur = longueurChaine(chaine)
      L'utilisation d'une valeur de retour permet de s'affranchir d'un pointeur.
    • En général, le paramètre entier est utilisé pour indiquer la taille du tableau passé en paramètre, qu'il est impossible de connaitre autrement.
      Dans ton cas, c'est un pointeur pour pouvoir le modifier, mais il doit/devrait être utiliser pour empêcher de lire en dehors du tableau de char(acters).
      ici, l'initialisation du pointé (longueur) devrait être la valeur 1000 (taille du tableau)


    Petits principes pour éviter de souffrir:
    Une variable de moins est une source d'erreur de moins.
    Dans ton code, tu écris:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        int longueur=0;
        int *ptLongueur=NULL;
        ptLongueur=&longueur;
        //...
        longueurChaine(chaine,ptLongueur);
    qui s'écrirait plutôt, en profitant de l'opérateur d'adressage &:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        int longueur=0;
        //...
        longueurChaine(chaine,&longueur);
    Une pointeur de moins est une source d'erreur de moins.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int longueur=0;
    //...
    longueur = longueurChaine(chaine)
    remarque: ici, l'initialisation est inutile, mais mieux vaut prendre l'habitude d'initialiser les variables, cela t'évitera d'oublier d'initialiser ce qui doit l'être.

    à présent, mon avis sur ta fonction.
    en reprenant mes conseils, elle devient:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int longueurChaine(char*chaine)
    {
        int i=0;
        while (chaine[i]!='\0')
        {
            i++;
        }
        return i;
    }
    plusieurs possibilités s'offrent à toi.
    1. Compacter le code comme une brute, mais ignorer l'overflow:
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      int longueurChaine(char*chaine)
      {
          int longueur;
          for (longueur=0; chaine[longueur]!='\0'; ++longueur);//ici le ; est l'instruction répétée. :)
          return longueur;
      }
    2. Te protéger de l'overflow:
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      int longueurChaine(char*chaine, int longueurMax)//et donner CHAINE_MAX en valeur de longueurMax dans l'appel
      {
          int longueur=0;
          while ( i<longueurMax && chaine[longueur]!='\0' )
          {
              longueur++;
          }
          if(longueur<longueurMax) return longueur;
          else return -1;//code d'erreur à vérifier. la chaine lue est trop grande pour le buffer... l'overflow à déjà été utilisé.
      //exit(-1) pourrait même être envisagé, pour bloquer l'overflow.
      }
    3. Combiner les deux:
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      int longueurChaine(char*chaine, int longueurMax)
      {
          int longueur;
          for (longueur=0; longueur<longueurMax && chaine[longueur]!='\0'; ++longueur);//ici le ; est l'instruction répétée. :)
          return (longueur<longueurMax) ? longueur : -1;
      }


    Pour la petite histoire, on reconnait un connaisseur du C++ d'un adepte du C à ses boucles for. En effet, en C++, ++i est préférable à i++, et strictement équivalent en C pour l'itération d'une boucle.
    Ne te méprends cependant pas, si la variable est bien incrémenté, la valeur de l'expression elle même n'est pas la même (si i==2, ++i == 3 et i++ == 2, mais dans les deux cas, i devient 3).

    Voilà, félicitation d'avoir tout lu, et bonne continuation.

    PS:à être bavard, je me suis fait doublé par l'ami pythéas

  4. #4
    Membre expérimenté Avatar de ManusDei
    Homme Profil pro
    vilain troll de l'UE
    Inscrit en
    Février 2010
    Messages
    1 624
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : vilain troll de l'UE

    Informations forums :
    Inscription : Février 2010
    Messages : 1 624
    Par défaut
    Je rajouterais, avant d'utiliser la version 3 de leternel, s'assurer d'avoir bien compris ce qu'il se passe, car en débutant c'est pas évident

    Pourquoi ++i est préférable à i++ en C++ ?

  5. #5
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 491
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 491
    Billets dans le blog
    1
    Par défaut
    +100 pour ne pas utiliser gets. Ca devrait être puni par la loi


    Le fait d'être constant est obligatoire en l'état actuel de la norme du C.
    Ce n'est plus vrai depuis le C99 et les VLA (Variable Length Arrays).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char[CHAINE_MAX] chaine;
    Tu as voulu écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char chaine[CHAINE_MAX] ;
    je pense

    Pas d'accord. En fait, c'est effectivement mieux de faire ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char chaine[CHAINE_MAX] = {0};
    Ainsi, tous les éléments vont être initialisés à 0. Et donc quelque soit la chaine qui sera écrite dedans, on aura un \0 derrière (sauf si bien sûr, on va jusqu'à écrasé le dernier élément du tableau. Dans ton code, seul le premier est à \0. Pas très utile. A la limite, écrire \0 sur le dernier élément du tableau, mais tant qu'à fait, tout initialiser à zéro.

    Un code exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int main(int argc, char *argv[])
    {
        int tableau[150];
        for(int i=0; i<150; printf("%d ", tableau[i++]) )
            ;
     
        puts("\n\n");
     
        int tableau_2[150] = {0};
        for(int i=0; i<150; printf("%d ", tableau_2[i++]) )
            ;
     
        return 0;
    }
    En général, le paramètre entier est utilisé pour indiquer la taille du tableau passé en paramètre, qu'il est impossible de connaitre autrement.
    Dans ton cas, c'est un pointeur pour pouvoir le modifier, mais il doit/devrait être utiliser pour empêcher de lire en dehors du tableau de char(acters).
    ici, l'initialisation du pointé (longueur) devrait être la valeur 1000 (taille du tableau)
    C'est une fonction strlen, on ne va pas lui passer la taille en paramètre non plus ^^ La fonction s'arrête quand elle rencontre une valeur particulière, \0 pour les chaines de caractères.

    Je ne vois pas ce que le paramètre longueurMax vient faire là. Strlen n'en prend pas, ça n'a rien à faire ici. Ce qu'il faut revoir, c'est la manière de gérer la chaine dans le main éventuellement, pas dans la fonction.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Pourquoi ++i est préférable à i++ en C++ ?
    ++i renvoie une référence sur i, i++ non. Mais je ne connais pas l'intérêt pour ce genre de boucle.

  6. #6
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Pourquoi ++i est préférable à i++ en C++ ?
    ++i renvoie une référence sur i, i++ non. Mais je ne connais pas l'intérêt pour ce genre de boucle.
    En C++ faire i++ revient à enregistrer la valeur de i, incrémenter i puis retourner la valeur enregistrée.
    Faire i++ est toujours plus lent (parfois aussi rapide dans certaines circonstances) que de faire ++i.

  7. #7
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 491
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 491
    Billets dans le blog
    1
    Par défaut
    En fait, il y a une entrée à ce sujet dans la FAQ C++, enjoy !

    http://cpp.developpez.com/faq/cpp/?p...GE_rapidite_pp

  8. #8
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Citation Envoyé par Bktero Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char[CHAINE_MAX] chaine;
    Tu as voulu écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char chaine[CHAINE_MAX] ;
    je pense
    oups...

    Citation Envoyé par Bktero Voir le message
    Pas d'accord. En fait, c'est effectivement mieux de faire ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char chaine[CHAINE_MAX] = {0};
    Ainsi, tous les éléments vont être initialisés à 0. Et donc quelque soit la chaine qui sera écrite dedans, on aura un \0 derrière (sauf si bien sûr, on va jusqu'à écrasé le dernier élément du tableau. Dans ton code, seul le premier est à \0. Pas très utile. A la limite, écrire \0 sur le dernier élément du tableau, mais tant qu'à fait, tout initialiser à zéro.

    Un code exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int main(int argc, char *argv[])
    {
        int tableau[150];
        for(int i=0; i<150; printf("%d ", tableau[i++]) )
            ;
     
        puts("\n\n");
     
        int tableau_2[150] = {0};
        for(int i=0; i<150; printf("%d ", tableau_2[i++]) )
            ;
     
        return 0;
    }
    Comme quoi, dix ans de C et cinq de C++, et j'en apprends encore.

    Citation Envoyé par Bktero Voir le message
    C'est une fonction strlen, on ne va pas lui passer la taille en paramètre non plus ^^ La fonction s'arrête quand elle rencontre une valeur particulière, \0 pour les chaines de caractères.

    Je ne vois pas ce que le paramètre longueurMax vient faire là. Strlen n'en prend pas, ça n'a rien à faire ici. Ce qu'il faut revoir, c'est la manière de gérer la chaine dans le main éventuellement, pas dans la fonction.
    On ne devrait jamais accéder à une case mémoire qu'on ne possède pas, même juste en lecture.
    Ce principe, certes trop violent, permet d'éviter des tas d'erreurs.
    Un tableau est alloué avec une taille fixe. Une fonction qui s'attend à un tableau doit recevoir un entier qui indique la taille d'allocation ou la taille réelle, selon l'information pertinente et connue.
    Ceci dit, ici, on ne fait que lire de la mémoire sans la copier, c'est sans risque majeur (sauf si l'intégralité de la mémoire est exempte de \0, ce qui m'étonnerait puisqu'elle doit contenir au moins le binaire et donc le \0...)

  9. #9
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 491
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 491
    Billets dans le blog
    1
    Par défaut
    Comme quoi, dix ans de C et cinq de C++, et j'en apprends encore.
    Et on en apprendra encore dans 10 ans, toi comme moi
    Petite remarque d'ailleurs sur ce mécanisme : ça ne marche qu'avec 0. Tu ne peux pas tout initialiser à 5 de manière standard. En fait, si tu mets autre chose que 0, ça écrit cette chose et ça complète avec des zéros. Voir ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int main(int argc, char *argv[])
    {
        int tableau_2[150] = {5, 7, 562, 12};
        for(int i=0; i<150; printf("%d ", tableau_2[i++]) )
            ;
     
        return 0;
    }

    On ne devrait jamais accéder à une case mémoire qu'on ne possède pas, même juste en lecture.[...]
    C'est pas totalement faux. Mais ici, c'est le jeu avec strlen ^^

  10. #10
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 794
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 794
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Bktero Voir le message
    En fait, si tu mets autre chose que 0, ça écrit cette chose et ça complète avec des zéros.
    Salut
    Je suis un peu rouillé (ça fait 5 ans que je ne fais plus de C m'étant converti à Python) mais il me semble que l'initialisation automatique ne se fait qu'avec des variables statiques ou globales.
    Accessoirement je ne me suis jamais posé la question, ayant toujours eu l'habitude de n'initialiser que quand c'était absolument nécessaire et, dans ce cas, de le faire manuellement...

    Citation Envoyé par Bktero Voir le message
    C'est pas totalement faux. Mais ici, c'est le jeu avec strlen ^^
    Euh là je ne suis plus trop ta pensée. Me semble que si on passe à strlen() (ou à une quelconque fonction similaire) un pointeur sur une chaine, on est sensé posséder tous les caractères de cette chaine même le '\0' final. Donc strlen() ou autre n'ira jamais lire un octet qui ne nous appartient pas. Donc la préconisation "ne jamais lire un octet qui n'est pas à nous" reste toujours valide même avec strlen() non ?
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  11. #11
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Non, l'une des préconditions est que la chaîne de caractère comporte un '\0' à sa fin, si ce n'est pas le cas, la chaîne de caractère n'est pas valide et on ne pourra pas utiliser de fonctions de string.h dessus.

    On lira donc tous les caractère jusqu'au '\0' inclus qu'ils nous appartiennent ou non.

  12. #12
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 794
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 794
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Non, l'une des préconditions est que la chaîne de caractère comporte un '\0' à sa fin, si ce n'est pas le cas, la chaîne de caractère n'est pas valide et on ne pourra pas utiliser de fonctions de string.h dessus.

    On lira donc tous les caractère jusqu'au '\0' inclus qu'ils nous appartiennent ou non.
    Ben oui mais si on passe à strlen() un truc qui n'est pas une chaine, c'est normal qu'il parte en torche et on ne peut pas lui reprocher, dans ce cas là, de ne pas respecter la préconisation en question...
    Ou, dit autrement, la préconisation "ne jamais lire un octet qui n'est pas à nous" sous-entend "sous réserve qu'on lui ait passé un paramètre adéquat"...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  13. #13
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 491
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 491
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Salut
    Je suis un peu rouillé (ça fait 5 ans que je ne fais plus de C m'étant converti à Python)
    Ah Python ! Un bien beau langage que je découvre petit à petit depuis le début de l'année et que j'aime beaucoup ! D'ailleurs, tu avais contribué à me faire apprendre ce langage avec les exemples alléchant envoyés par MP

    Citation Envoyé par Sve@r Voir le message
    mais il me semble que l'initialisation automatique ne se fait qu'avec des variables statiques ou globales.
    Si je me souviens bien, les variables globales sont effectivement initialisées implicitement à zéro, ça doit être aussi le cas pour les tableaux.
    Si par variables statiques, tu entends tableaux avec [], alors oui la technique que je montre ne marque qu'avec des tableaux de cette sorte. Tu peux faire ça qu'à l'initialisation, et non à une affectation.

    Citation Envoyé par Sve@r Voir le message
    Accessoirement je ne me suis jamais posé la question, ayant toujours eu l'habitude de n'initialiser que quand c'était absolument nécessaire et, dans ce cas, de le faire manuellement...
    Des fois, c'est pratique de mettre des zéros partout ^^ Surtout avec des chaines de caractères.


    Citation Envoyé par Sve@r Voir le message
    Euh là je ne suis plus trop ta pensée. Me semble que si on passe à strlen() (ou à une quelconque fonction similaire) un pointeur sur une chaine, on est sensé posséder tous les caractères de cette chaine même le '\0' final. Donc strlen() ou autre n'ira jamais lire un octet qui ne nous appartient pas. Donc la préconisation "ne jamais lire un octet qui n'est pas à nous" reste toujours valide même avec strlen() non ?
    Tout est dans le censé. Tu ne dis pas à strlen quelle est la taille allouée, elle assume qu'elle va trouver un \0 et rien d'autre. Ce n'est donc pas à elle mais à toi de t'assurer que ce marqueur de fin est dans la zone allouée et réservée à ta chaine. C'est tout ce que je voulais dire

  14. #14
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    Mai 2004
    Messages
    10 150
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : Mai 2004
    Messages : 10 150
    Par défaut
    C'est drole, dans la plupart des codes presentes ici (je ne les ai pas tous lus attentivement), il y a le meme bug que dans (la plupart des implementations de) strlen : un magnifique plantage sur une chaine NULL.

    Alors oui, je sais, on peut concevoir que demander la longueur de NULL est une connerie, mais de la a ce que la fonction plante le programme, il y a un monde.

    Un simple test :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if (chaine == NULL)
      return -1;
    ou equivalent permet de se premunir de l'erreur.
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  15. #15
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 491
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 491
    Billets dans le blog
    1
    Par défaut
    Remarque très judicieuse !

  16. #16
    Membre confirmé Avatar de zozoman
    Homme Profil pro
    Futur ex-prof
    Inscrit en
    Décembre 2007
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Allier (Auvergne)

    Informations professionnelles :
    Activité : Futur ex-prof
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 119
    Par défaut Merci
    Merci à vous pour ces réponses détaillées.

    Je n'ai pas utilisé l'instruction return car dans les ouvrage que j'ai, il est précisé que le return sert pour gérer les erreurs.

    Vous me dites de ne pas utiliser gets mais si je déclare un tableau de char[100] pour ma chaine et que je rentre plus de 100 caractères, je ne risque pas d'avoir une erreur ?

    Autre remarque : pour voir la fin de la chaine, j'utilise la condition chaine[i]!='\0'
    or lorsque je rentre ma chaine, j'appuie sur entrée, il y a donc les caractères : '\0' et '\n' dans le buffer : pourquoi ne pas tester avec chaine[i]!='\n' ?

    Merci pour vos réponses

  17. #17
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Citation Envoyé par zozoman Voir le message
    Je n'ai pas utilisé l'instruction return car dans les ouvrage que j'ai, il est précisé que le return sert pour gérer les erreurs.
    C'est une logique qui se défend. Auquel cas, on retourne 0, plutôt que la valeur calculée, pour signaler un succes. cela permet d'écrire un test
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    if( 0 != strlen(...) ) {
        /* traiter erreur */
    }
    Citation Envoyé par zozoman Voir le message
    Vous me dites de ne pas utiliser gets mais si je déclare un tableau de char[100] pour ma chaine et que je rentre plus de 100 caractères, je ne risque pas d'avoir une erreur ?
    Mais justement, avec gets aussi, tu as cette erreur, et pour le coup, tu ne pourrait pas la bloquer.

    Citation Envoyé par zozoman Voir le message
    Autre remarque : pour voir la fin de la chaine, j'utilise la condition chaine[i]!='\0'
    or lorsque je rentre ma chaine, j'appuie sur entrée, il y a donc les caractères : '\0' et '\n' dans le buffer : pourquoi ne pas tester avec chaine[i]!='\n'?
    Strictement parlant, la chaine se termine au '\0'. Si tu veux ignorer le '\n', il faut remonter à sa position, et écrire un '\0' à la place.

    Il faut bien comprendre que strlen est une fonction qu'il faut coder avec énormément de soin, parce qu'elle sert souvent à calculer la longueur de protection pour appeler les autres fonctions.
    (elle n'est pas appelée en interne, pour permettre de parcourir la chaine une fois plutot que deux)

    Dans son contrat strlen calcule la longueur de la chaine au sens du C, donc du tableau de caractere non nuls (différents de '\0', qui vaut normalement 0)

  18. #18
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 794
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 794
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par zozoman Voir le message
    Je n'ai pas utilisé l'instruction return car dans les ouvrage que j'ai, il est précisé que le return sert pour gérer les erreurs.
    Le return sert à renvoyer une valeur à l'appelant. On peut effectivement l'utiliser pour indiquer à l'appelant qu'il y a eu un souci. Dans ce cas on renvoie une valeur impossible (comme par exemple -1) mais ce n'est pas son but premier...

    Citation Envoyé par zozoman Voir le message
    Vous me dites de ne pas utiliser gets mais si je déclare un tableau de char[100] pour ma chaine et que je rentre plus de 100 caractères, je ne risque pas d'avoir une erreur ?
    Justement, utilise fgets(chaine, 100, stdin). fgets() bloque automatiquement la saisie dès que 99 caractères sont rentrés (il garde la place pour le '\0')...

    Citation Envoyé par zozoman Voir le message
    Autre remarque : pour voir la fin de la chaine, j'utilise la condition chaine[i]!='\0'
    or lorsque je rentre ma chaine, j'appuie sur entrée, il y a donc les caractères : '\0' et '\n' dans le buffer : pourquoi ne pas tester avec chaine[i]!='\n' ?
    Parce que le '\n' provient du "entrée" mais toutes les chaines ne sont pas forcément saisies au clavier.
    En bref: toute chaine saisie via gets() ou fgets() possèdera '\n' puis '\0' et toute chaine quelle qu'elle soit possèdera '\0'. Donc rester dans le cas général et chercher '\0' fera que ta fonction sera utilisable de partout.
    Ce que tu peux faire, en revanche, c'est rajouter en amont la suppression du '\n'
    Exemple
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char chaine[100];
    char *pt;
    fgets(chaine, 100, stdin);
    if ((pt=strrchr(chaine, '\n')) != NULL) *pt='\0';
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  19. #19
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    En bref: toute chaine saisie via gets() ou fgets() possèdera '\n' puis '\0'....
    C'est une différence entre gets() et fgets() : gets() ne met pas le '\n' dans le buffer alors que fgets() le met.

  20. #20
    Membre émérite
    Avatar de Kirilenko
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2011
    Messages
    234
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 234
    Par défaut Chaîne nulle en paramètre de la fonction
    Citation Envoyé par gangsoleil Voir le message
    C'est drole, dans la plupart des codes presentes ici (je ne les ai pas tous lus attentivement), il y a le meme bug que dans (la plupart des implementations de) strlen : un magnifique plantage sur une chaine NULL.

    Alors oui, je sais, on peut concevoir que demander la longueur de NULL est une connerie, mais de la a ce que la fonction plante le programme, il y a un monde.

    Un simple test :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if (chaine == NULL)
      return -1;
    ou equivalent permet de se premunir de l'erreur.
    Je ne suis pas tout à fait d'accord. Déjà, je ne pense pas qu'on puisse parler de bogue, puisque la fonction strlen de la bibliothèque standard de C est généralement optimisée à la main afin de passer correctement sur toutes les implémentations, et notamment dans le cas où les contraintes de performances seraient élevées (et, sur certaines architectures, un branchement conditionnel peut se révéler conséquent...). De plus, ce code n'obéit pas aux contraintes qui aboutissent à l'élaboration d'un seul point de sortie, et il agrandit le domaine de définition de la fonction, ce qui peut se révéler problématique pour l'appelant (beaucoup de codes différents à traiter au cas par cas). C'est un thème subjectif, mais je reste persuadé que, pour une fonction de la bibliothèque standard de C (qui se doit d'être une bibliothèque « générique »), il serait préjudiciable de privilégier la sécurité aux dépens des performances, alors que l'appelant peut très bien faire la vérification à sa place (et d'ailleurs, peut-être l'a-t-il déjà fait, ce qui, dans ce cas, ferait une vérification redondante). Après, on peut toujours faire cette vérification sur une implémentation maison (et notamment lorsqu'on fait un semblant d'orienté objet) parce que l'utilisateur n'a pas les clés en main pour faire les tests nécessaires. Mais ça ne me semble pas être le cas ici. Pour moi, ce « bogue » est par conséquent à nuancer (même si, pour « débuter », c'est peut-être mieux). Enfin, pour être à cheval sur le vocabulaire : strlen calcule la longueur d'une chaîne, or, NULL n'est pas une chaîne.

    Voilà, je retourne dans ma caverne.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    #include <stdio.h>
    #include <stdlib.h>
     
    static size_t
    my_strlen(const char *s)
    {
    	/*
    	 * Si on fait une vérification ici, on se heurte à une vérification
    	 * redondante inutile, puisque `s' a déjà été testée dans la fonction
    	 * `f'. Dès qu'il y a une redondance, c'est qu'il y a un problème 
    	 * quelque part.
    	 */
    	size_t i;
    	for (i = 0UL; s[i] != '\0'; ++i) 
    		;
    	return i;
    }
     
    size_t
    f(size_t nMax)
    {
    	size_t      nSize = -1;
    	char *const s     = malloc(nMax);
    	if (s != NULL) {
    		fgets(s, nMax, stdin);
    		nSize = my_strlen(s);
    		free(s);
    	}
    	return nSize;
    }
    Quant à l'utilisation de la fonction gets, elle n'est même plus dans la norme C11. Il faut donc la bannir définitivement.
    Récursivité en C : épidémie ou hérésie ?

    "Pour être un saint dans l'Église de l'Emacs, il faut vivre une vie pure. Il faut se passer de tout logiciel propriétaire. Heureusement, être célibataire n'est pas obligé. C'est donc bien mieux que les autres églises" - Richard Stallman

Discussions similaires

  1. Optimisation d'une fonction
    Par BNS dans le forum C++
    Réponses: 7
    Dernier message: 15/12/2007, 22h25
  2. Réponses: 6
    Dernier message: 27/06/2007, 16h44
  3. Trou de mémoire : fonction strlen()
    Par bit_o dans le forum C
    Réponses: 3
    Dernier message: 30/04/2007, 23h20
  4. Comportement bizarre de la fonction strlen
    Par clampin dans le forum C
    Réponses: 4
    Dernier message: 30/12/2006, 14h00
  5. [PHP-JS] Fonction strlen en php
    Par viny dans le forum Langage
    Réponses: 20
    Dernier message: 04/10/2006, 14h09

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo