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

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  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 : 54
    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 493
    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 493
    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 493
    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 493
    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 493
    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 493
    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 ^^

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