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 :

écrire des chaînes de longueur indéfinie


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Par défaut écrire des chaînes de longueur indéfinie
    Je cherche le moyen le plus simple et universel de coder en C la fonctionnalité habituelle dans d'autre langages qui n'impose pas de prédéfinir la longueur d'une chaîne à composer. Je veux dire qu'en C pour obtenir le texte d'une donnée (fonction du type tostring) ou composer un texte quelconque (genre avec sprintf) il faut passer une chaîne tampon de longueur fixe. Ce qui pour moi est à la fois ennuyeux et erronné car cela force à surdimensionner, et systématiquement, sans compter que parfois il n'y a pas du tout de longueur max prédéfinie (ça dépend d'un contenu variable: quelle est la longueur max de l'expression textuelle d'un tableau? et je parle même pas du cas général de sprintf).

    Mon objectif est d'envelopper (wrap) les fonctions de la lib dans des fonctions à moi, du type int_tostring(int i) (conversions) ou format(char * fmt, ...) (cas général de type sprintf). J'ai découvert le code %n qui permet de savoir combien d'octets ont été écris, ce qui est une grande aide. Mais c'est à postériori donc il faut malgré tout avoir d'abord passé une chaîne de longueur fixe. Comment font les auteurs de langages implantés en C?

    Touts pistes bienvenues,
    merci,
    Denis

  2. #2
    Membre Expert
    Avatar de kwariz
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Octobre 2011
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Par défaut
    Salut,

    tu peux déjà consulter comment cette fonction est implémentée dans la gnulib, il s'agit de vasnprintf (v: paramètres passés en va_list, a: allocation automatique, s: dans une chaine, n:contrainte de longueur) : http://git.sv.gnu.org/gitweb/?p=gnul...b/vasnprintf.c - http://www.gnu.org/software/gnulib/M...ule=vasnprintf


    Tu vois que cela devient vite complexe pour faire quelque chose de portable et généraliste. Dans la gnulib sont disponibles des fonctions qui manquent à certains systèmes, des implémentations de fonctions standards mais plus sécurisées ou avec un comportement un peu différent.

  3. #3
    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
    Je cherche le moyen le plus simple et universel de coder en C la fonctionnalité habituelle dans d'autre langages qui n'impose pas de prédéfinir la longueur d'une chaîne à composer.
    J'ai envie de dire : bienvenu en C

    C est un langage où la gestion de la mémoire est à ta charge. C'est assez désagréable pour faire des petits programmes où tu aimerais pouvoir ne pas te soucier de la longueur des chaines ; tu ne peux pas à moins de sortir la grosse cavalerie. Si tu ne veux pas te soucier de la gestion de la mémoire, le C n'est pas le langage qu'il te faut. C'est une des raisons pour lesquelles j'ai envie d'apprendre Python mais le temps me manque !


    il faut passer une chaîne tampon de longueur fixe. Ce qui pour moi est à la fois ennuyeux et erronné car cela force à surdimensionner, et systématiquement, sans compter que parfois il n'y a pas du tout de longueur max prédéfinie
    Si tu veux faire faire une couche d'abstraction, tu retrouveras le problème dans ta couche d'abstraction : il faudra y prévoir un peu large à chaque fois pour ne pas risquer un débordement.

  4. #4
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 393
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 393
    Par défaut
    En pur standard C90, la solution classique est:
    • strlen() de tous les arguments chaînes de caractères
    • plus un surdimensionnement constant pour chaque nombre
    • plus longueur des parties autres que spécificateurs dans ta chaîne de format
    • plus caractère nul terminal.


    En standard C99, tu appelles snprintf() avec une taille nulle et un buffer nul, elle retournera une longueur. Tu alloues cette longueur +1.

    Sinon tu as des fonctions spécifiques dans certains environnements: asprintf() sous certains unixoïdes (wrappe malloc()+sprintf()), _scprintf() sous Visual Studio (retourne la taille nécessaire), etc.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  5. #5
    Membre confirmé
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    En pur standard C90, la solution classique est:
    • strlen() de tous les arguments chaînes de caractères
    • plus un surdimensionnement constant pour chaque nombre
    • plus longueur des parties autres que spécificateurs dans ta chaîne de format
    • plus caractère nul terminal.
    Bien, je me voyais faire quelque chose comme ça. Ce serait un tout petit peu plus facile dans mon cas, car j'ai déjà un type 'Text' qui contient son "poids" (j'évite le terme longueur pour éviter la confusion entre longueur et index de chaîne et longueur et index de textes / caractères, dès qu'on sort de l'ascii). Du coup, je n'ai pas besoin de strlen().

    En standard C99, tu appelles snprintf() avec une taille nulle et un buffer nul, elle retournera une longueur. Tu alloues cette longueur +1.

    Sinon tu as des fonctions spécifiques dans certains environnements: asprintf() sous certains unixoïdes (wrappe malloc()+sprintf()), _scprintf() sous Visual Studio (retourne la taille nécessaire), etc.
    Aha! snprintf() a l'air d'être ce que je cherchais (je me disais bien qu'il y avait un outil quelque part pour éviter de réinventer la roue). Je vais étudier cela.

    Merci de vos infos,
    Denis

  6. #6
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 393
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 393
    Par défaut
    Le problème de snprintf(), c'est qu'en tant que fonction C99, elle n'existe pas telle quelle sous Visual Studio, pas même la version 2010: Microsoft s'assoit sur C99.

    Heureusement, il y a moyen de reproduire le comportement (bien qu'avec de moins bonnes performances, vu qu'on traite la chaîne deux fois) avec un tel code.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Membre confirmé
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Par défaut réponse prometteuse
    Citation Envoyé par Médinoc Voir le message
    [...]
    En standard C99, tu appelles snprintf() avec une taille nulle et un buffer nul, elle retournera une longueur. Tu alloues cette longueur +1.
    Alors, oui: avec vsnprintf, ça roule:
    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
     
    char * format (char * form, ...) {
       va_list args ; va_start (args, form) ;
       char *s, *s0 ;
       Uint weight ;
     
       // Determine weight (count of bytes) in output (NUL excluded):
       s0 = malloc (1 * sizeof(char)) ;
       assert (s0 != NULL) ;
       weight = vsnprintf (s0, 1, form, args) ;
       printf ("weight:%i\n", weight) ;
       assert (weight > 0) ;
       weight ++ ;                                  // for NUL terminator
       free (s0) ; s0 = NULL ;
     
       // Write into new string of that weight:
       s = malloc (weight * sizeof(char)) ;
       assert (s != NULL) ;
       weight = vsprintf (s, form, args) ;
    //~    printf ("weight:%i\n", weight) ;
     
       va_end (args) ;
       return s ;
    }
     
    void test () {
       char * s = format ("int:%05i float:%+9.3f string:'%9s'",
          123, 1.23, "123") ;
       puts (s) ;
    }
    Petits points à éclaircir:

    * Je n'ai pas trouvé moyen de repasser la liste d'arguments variadique à snprintf, alors j'ai dû utiliser vsnprintf avec les outils de stdarg.h.

    * Impossible de passer NULL comme string cible: --> segfault direct. Même chose si je passe une string de taille nulle.

    * Si j'utilise (v)sprintf (sans le 'n') et que je passe pas une première string de taille suffisante, j'obtiens une erreur de glibc:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    "Glibc detected *** free(): invalid next size (fast) ..."
    pour laquelle il y a largement de quoi lire (en anglais) sur le web, mais pas de solution évidente. C'est évidemment dû à un buffer overflow, mais l'erreur bizarrement pointe sur le free(S0) (chez les autres aussi).

    J'ai asprintf et cie à disposition sur mon système, mais je préférerais rester aussi standard que possible: raison pour laquelle j'essayais de me passer du 'v' et du 'n' de vnsprintf. Dans l'idéal, mon code devrait pouvoir compiler avec n'importe quel compilateur conforme, ou au moins n'importe quel variante de gcc, sur n'importe quelle plateforme, même ancienne.

    Denis

  8. #8
    Membre Expert
    Avatar de kwariz
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Octobre 2011
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Par défaut
    Citation Envoyé par denispir Voir le message
    J'ai asprintf et cie à disposition sur mon système, mais je préférerais rester aussi standard que possible: raison pour laquelle j'essayais de me passer du 'v' et du 'n' de vnsprintf. Dans l'idéal, mon code devrait pouvoir compiler avec n'importe quel compilateur conforme, ou au moins n'importe quel variante de gcc, sur n'importe quelle plateforme, même ancienne.

    Denis
    Salut,

    une ou deux remarques en passant :

    *** assert n'est pas fait pour tester les erreurs runtime ou user !
    assert existe pour aider le développeur à trouver les erreurs de conception/programmation uniquement ***
    Suivant les options de compilations aucun code ne sera généré pour assert.



    Obtenir un code portable est complexe mais pas compliqué. D'autant plus que la fonction que tu essayes de mettre au point existe déjà et est mise à disposition dans la gnulib (pas la glibc ... regarde un lien que j'ai précédemment posté). Avec ça tu es sûr que :

    1. ça fonctionne
    2. c'est maintenu, testé, supporté
    3. c'est multiplateforme et multicompilateur.

    Si tu te restreins à gcc ... c'est encore plus de bonheur pour toi la gnulib

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Débutant] Déclarer des chaînes de longueur fixe
    Par Claude_Azoulai dans le forum C#
    Réponses: 9
    Dernier message: 29/03/2013, 18h49
  2. Réponses: 4
    Dernier message: 06/09/2005, 21h41
  3. [D6] Comment remplacer des chaînes dans un fichier ?
    Par fabien25 dans le forum Langage
    Réponses: 4
    Dernier message: 15/06/2005, 13h37
  4. Réponses: 9
    Dernier message: 05/04/2005, 09h39
  5. Passage des chaînes de caractères à une application CGI
    Par Thom@s dans le forum Composants VCL
    Réponses: 10
    Dernier message: 03/12/2004, 00h13

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