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 :

segfault sur un sprintf


Sujet :

C

  1. #1
    Membre actif
    Profil pro
    ingénieur
    Inscrit en
    Novembre 2011
    Messages
    165
    Détails du profil
    Informations personnelles :
    Localisation : France, Tarn (Midi Pyrénées)

    Informations professionnelles :
    Activité : ingénieur

    Informations forums :
    Inscription : Novembre 2011
    Messages : 165
    Points : 259
    Points
    259
    Par défaut segfault sur un sprintf
    Bonjour à tous,

    J'ai une segmentation fault sur le sprintf lorsque j'execute le code suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <stdio.h>
     
    int main () {
     
        char *pt="Pb0";
        sprintf(pt,"Pb0%1d",1);
     
        return 0;
    }
    quelqu'un voit pourquoi?

    De plus la ligne
    me surprend: ne serait-il pas plus correct d'écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char *pt=NULL;
    *pt="Pb0";
    ?

    Merci d'avance ^^

  2. #2
    Membre confirmé
    Avatar de deletme
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Indre et Loire (Centre)

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2011
    Messages : 257
    Points : 519
    Points
    519
    Par défaut
    Salut,

    J'ai une segmentation fault sur le sprintf lorsque j'execute le code suivant:
    Tu manipules une chaine de caractère à l'aide d'un pointeur, il faut donc faire attention à l'endroit où tu vas écrire et la quantité que tu vas écrire. Ici le dernier point est respecté... mais pas le premier

    Tu donnes a ta fonction sprintf l'adresse du pointeur (qui est à la discrétion de ton compilateur) et non l'adresse de ta chaine :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <stdio.h>
     
    int main () {
     
        char *pt="Pb0";
        sprintf(&pt,"Pb0%1d",1);
     
        return 0;
    }
    L'adresse sur laquelle pointe pt est caractérisée par &pt.
    "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live."
    - Martin Golding
    Traduction obligatoire : "Toujours écrire du code en gardant en tête que le mec qui en assurera la maintenance est un psychopathe violent qui connait votre adresse"

  3. #3
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Bonjour,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char *pt="Pb0";
    sprintf(pt,"Pb0%1d",1)
    La string litteral "Pb0" est placée en mémoire constante. Le pointeur vise donc une zone mémoire qui n'est pas modifiable. Le sprintf() essaye et se fait bouler : segmentation fault. Voir la : http://c.developpez.com/faq/?page=st...RINGS_pointeur

    EDIT : d'ailleurs le compilateur te donne une indication sur la ligne de création de la variable :
    D:\main.c|5|warning: initialization discards 'const' qualifier from pointer target type [enabled by default]|
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char *pt=NULL;
    *pt="Pb0";
    Un compilateur bien réglé émet un warning sur la seconde ligne car tu essayes de mettre une adresse (celle de la chaine littérale) dans un char:
    D:\main.c|9|warning: assignment makes integer from pointer without a cast [enabled by default]|
    Et l'exécution plantera. Ici, tu tentes de déréfencer un pointeur qui ne pointe vers rien (NULL). Tu ne peux pas écrire à cet endroit : crash.

    Dans les deux cas, il faut un pointeur vers une zone modifiable : malloc() ou un char[].


    EDIT : deletme, je pense que tu as tord. Ton code donne l'adresse du pointeur, pas celui du PO. Ce qui me fait dire ça ? Ton code compile avec des erreurs :
    D:\main.c||In function 'main': |
    D:main.c|5|warning: initialization discards 'const' qualifier from pointer target type [enabled by default]|
    D:\main.c|6|warning: passing argument 1 of 'sprintf' from incompatible pointer type [enabled by default]|
    c:\program files (x86)\codeblocks\mingw\bin\..\lib\gcc\mingw32\4.7.1\..\..\..\..\include\stdio.h|262|note: expected 'char *' but argument is of type 'char **'|
    ||=== Build finished: 0 errors, 2 warnings (0 minutes, 0 seconds) ===|

  4. #4
    Membre confirmé
    Avatar de deletme
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Indre et Loire (Centre)

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2011
    Messages : 257
    Points : 519
    Points
    519
    Par défaut
    Oui en effet, en reconfigurant correctement mon compilateur (nouvelle installation ) ça warning ^^

    Pour rester en statique, passe par un tableau (de taille suffisante) sinon tu peux faire de l'allocation dynamique.

    EDIT :

    Bon, en prêtant un peu plus attention à mon code généré, voici deux alternatives :

    En dynamique :
    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
    #include <stdio.h>
    #include <stdlib.h>
     
    int main () {
     
        char *pt;
     
        pt = malloc(sizeof(char) * 10); // allocation dynamique de l'espace mémoire
     
        if(pt == NULL)
            return EXIT_FAILURE; // l'allocation a échoué
        else
        {
            sprintf(pt,"test");
            printf("%s\r\n", pt);
        }
     
        free(pt); // libération de la mémoire allouée dynamiquement
     
        return EXIT_SUCCESS;
    }
    En statique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <stdio.h>
     
    int main () {
     
        char pt[10];
     
        sprintf(pt,"test");
        printf("%s\r\n", pt);
     
        return EXIT_SUCCESS;
    }
    "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live."
    - Martin Golding
    Traduction obligatoire : "Toujours écrire du code en gardant en tête que le mec qui en assurera la maintenance est un psychopathe violent qui connait votre adresse"

  5. #5
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Je préfère ça

    Deux petites remarques : je pense que tu n'as pas besoin d''\r', '\n' est converti comme il faut par le C ; puts(pt) c'est plus court.

    Il faut toutefois faire attention avec ce genre de code à ne pas écrire trop avec sprintf() car si on dépasse de la zone réservée, on risque (encore) une erreur de segmentation. Soit on fait attention, soit on utilise snprintf().

    "Un petit vote négatif m'apprendra à répondre trop vite ^^" --> Si tu insistes...

  6. #6
    Membre régulier Avatar de Mipwiq
    Homme Profil pro
    Inscrit en
    Avril 2013
    Messages
    42
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Avril 2013
    Messages : 42
    Points : 81
    Points
    81
    Par défaut
    Citation Envoyé par deletme Voir le message
    Bon, en prêtant un peu plus attention à mon code généré, voici deux alternatives :

    En dynamique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <stdio.h>
    #include <stdlib.h>
     
    int main () {
     
        char *pt = malloc(sizeof(char) * 10);
     
        sprintf(pt,"test");
        printf("%s\r\n", pt);
     
        return 0;
    }
    Fait attention quand tu utilises malloc n'oublie pas de n'allouer que le nécessaire et surtout de free à la fin de l'utilisation du pointeur.

    EDIT: Aussi ne jamais oublier les valeurs de retour.

  7. #7
    Membre confirmé
    Avatar de deletme
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Indre et Loire (Centre)

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2011
    Messages : 257
    Points : 519
    Points
    519
    Par défaut
    Fait attention quand tu utilises malloc n'oublie pas de n'allouer que le nécessaire et surtout de free à la fin de l'utilisation du pointeur.
    oui tu as raison, les modifications ont été apportées au code.
    "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live."
    - Martin Golding
    Traduction obligatoire : "Toujours écrire du code en gardant en tête que le mec qui en assurera la maintenance est un psychopathe violent qui connait votre adresse"

  8. #8
    Membre actif
    Profil pro
    ingénieur
    Inscrit en
    Novembre 2011
    Messages
    165
    Détails du profil
    Informations personnelles :
    Localisation : France, Tarn (Midi Pyrénées)

    Informations professionnelles :
    Activité : ingénieur

    Informations forums :
    Inscription : Novembre 2011
    Messages : 165
    Points : 259
    Points
    259
    Par défaut
    merci pour vos explications, c'est beaucoup plus clair maintenant

  9. #9
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Si le problème est résolu, merci de cliquer sur le bouton en bas de l'écran

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Off-topic: -Wwrite-strings est maintenant activé par défaut? Cool!
    Ces versions récentes de gcc m'ont l'air de plus en plus intéressantes.
    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.

  11. #11
    Membre actif
    Profil pro
    ingénieur
    Inscrit en
    Novembre 2011
    Messages
    165
    Détails du profil
    Informations personnelles :
    Localisation : France, Tarn (Midi Pyrénées)

    Informations professionnelles :
    Activité : ingénieur

    Informations forums :
    Inscription : Novembre 2011
    Messages : 165
    Points : 259
    Points
    259
    Par défaut
    J'enlève provisoirement le résolu pour un petit détail: lorsque vous dites "La string litteral "Pb0" est placée en mémoire constante. Le pointeur vise donc une zone mémoire qui n'est pas modifiable." je ne suis pas sûr de bien comprendre comment est déterminé si la zone mémoire est modifiable ou pas et pourquoi en C on ne peut pas le savoir (d'après la FAQ).

  12. #12
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2013
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juillet 2013
    Messages : 9
    Points : 16
    Points
    16
    Par défaut
    Un élément de réponse : ta mémoire est décomposée en plusieurs zones deux d'entre elle sont la pile et le tas. Dans la pile , l'allocation de la mémoire est automatique et "non rémanente" c'est a dire que les variables déclarées dans cette zone mémoire disparaissent après l'apelle à la fonction dans laquelle elles sont déclarées. Dans le tas, c'est exactement le contraire. La pile correspond à la "zone de mémoire non modifiable".
    Lorsque tu faisait
    le mode d'allocation est automatique, soit dans la pile, et le compilo va demander a réserver de la mémoire exactement pour 'P','b','0' et '\0' sans rien pouvoir modifier après(il aurait fallu que tu fasse char chaine[10] pour forcer le nombre de case a 10 et ainsi mettre des chaines de 9 char sans compter le \0, mais la encore tu ne peux plus modifier la taille de ton tableau).
    Pour l'autre commande
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    char *pt = malloc(sizeof(char) * 10);
    //ou
    char *pt = (char*)malloc(sizeof(char) * 10); /*est a priori plus correct, 
    car elle permet d'avoir plus de vérifications et de warnings à la compilation et d'éviter 
    de finir par mettre n'importe quoi dans  pt.*/
    Elle réserve de la mémoire dans le tas. Ici tant que tu ne dis pas explicitement "je n'ai plus besoin d'utiliser cette zone de mémoire" avec free(void*), elle conserve les données que tu a mise. Cette zone de mémoire est modifiable avec la fonction void*
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    realloc(void *ptr, size_t size)
    Dans tout les cas cette modification ne se fait pas de manière automatique(essayer d'ecrire plus loin que le tableau fera au mieu un segfault.).

    Edit : j'ai fais un amalgame entre la pile et la zone de mémoire constante qui sont en réalité séparées, je vous prie de m'en excuser, voire 2 posts plus bas pour de meilleurs explications.

  13. #13
    Membre actif
    Profil pro
    ingénieur
    Inscrit en
    Novembre 2011
    Messages
    165
    Détails du profil
    Informations personnelles :
    Localisation : France, Tarn (Midi Pyrénées)

    Informations professionnelles :
    Activité : ingénieur

    Informations forums :
    Inscription : Novembre 2011
    Messages : 165
    Points : 259
    Points
    259
    Par défaut
    excellent post, merci ^^

  14. #14
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Excellent post.... mais faux.

    Il y a en fait 3 zones pour la mémoire. Il y a d'un côté la pile et le tas, qui sont modifiable ; de l'autre côté il y a la mémoire non modifiable, qui contient du code ou des données.

    La plupart des variables à l'intérieur d'une fonction sont déclarées sur la pile (les const, static et globales je crois que non mais j'ai pas trop le temps de chercher dans la norme) et disparaissent en sortie de la fonction. C'est le principe de scope.

    Tu peux réserver de la mémoire dans le tas avec l'intérêt que cette zone ne se "nettoie" pas toute seule. Tu peux donc garder des données accessible d'une fonction à l'autre. Cela passe par les fonctions malloc(), calloc(), realloc() et free().

    Pour accéder à ces zones, il te faut une variable (pour simplifier, qui est dans la pile) pour garder trace de l'adresse. C'est un pointeur. Si tu n'as plus de pointeur vers une zone du tas, il y a une fuite mémoire.

    La mémoire constante est une zone spéciale où on met.... les constantes. Les chaines littérales font partie de ces constantes. Donc quand le compilateur en trouve dans ton code, comme "hello, world", il la met là-bas et connait l'adresse dans cette mémoire non modifiable. C'est la norme qui définit ce comporte.

    Ainsi :

    On déclare une variable sur la pile, pt, la chaine littérale est mise en mémoire constante et la variable contient l'adresse.

    Le C sait que la chaine littérale est en mémoire constante mais il ne peut pas savoir que pt pointe vers une telle zone. C'est ce que dit la FAQ. Ainsi, il ne t'empêchera pas d'y écrire via ce pointeur mais ça va planter.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char *pt = malloc(sizeof(char) * 10);
    char *pt = (char*)malloc(sizeof(char) * 10);
    Le cast est inutile en C (ce n'est pas le cas en C++). Encore une fois, une variable sur la pile et une zone réservée dans le tas. Le pointeur contient l'adresse de cette zone. Le tas est modifiable, ça ne plantera pas d'y écrire.

    J'espère n'avoir pas dit de bêtise et avoir été clair.

  15. #15
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2013
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juillet 2013
    Messages : 9
    Points : 16
    Points
    16
    Par défaut
    Citation Envoyé par Bktero Voir le message
    Le cast est inutile en C (ce n'est pas le cas en C++).
    Je n'ai jamais dit (bien au contraire) que le code sans le cast est faux. Par contre il me semble que cela permet au compilateur de vérifier que l'on met toujours le bon type de variable ce en quoi je peut tout à fait me tromper(j'avoue ne pas avoir tester), et de lever des warnings pour nous empêcher de faire des bêtises plus tard lorsque l'on essaiera par exemple d'y stocker un int. Je suis désolé de la GROSSE imprécision sur les zones de la mémoire de mon poste précédent qui le rend en grande partie faux . Je vais aller réviser la norme

  16. #16
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Je n'ai pas dit que c'était faux, j'ai dit que c'était inutile. On a souvent parlé de ça sur Developpez, une recherche sur le forum pourrait remontrer des résultats à ce sujet.

    Et sinon, voici deux liens qui illustrent plutôt bien les raisons pour lesquelles on ne devrait pas caster le retour :
    http://stackoverflow.com/questions/6...sult-of-malloc
    http://c-faq.com/malloc/mallocnocast.html

    Note d'ailleurs qu'un cast explicite empêche en bonne partie le compilateur de vérifier les compatibilités. Par exemple, tu n'auras pas de warning sur le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    long n = 314;
    char *p = (char*) n;
    On ne devrait presque jamais faire de cast explicite. Si on le fait, c'est qu'on a une bonne raison et le compilateur se dit la même chose.

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Citation Envoyé par gavarax Voir le message
    Pour l'autre commande
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    char *pt = malloc(sizeof(char) * 10);
    //ou
    char *pt = (char*)malloc(sizeof(char) * 10); /*est a priori plus correct, 
    car elle permet d'avoir plus de vérifications et de warnings à la compilation et d'éviter 
    de finir par mettre n'importe quoi dans  pt.*/
    FAUX.
    Sans le cast, pt n'acceptera que quelque chose compatible avec son type, c'est-à-dire char* ou void*.
    Avec le cast, tu peux mettre n'importe quoi dans pt: int*, const char*, int, tout y passe.
    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.

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

Discussions similaires

  1. segfault sur une boost::python::list
    Par psycofdj dans le forum Boost
    Réponses: 1
    Dernier message: 15/10/2008, 00h41
  2. Segfault sur un int?
    Par lguignar dans le forum Débuter
    Réponses: 9
    Dernier message: 18/07/2008, 10h03
  3. Segfault sur un int?
    Par lguignar dans le forum C++
    Réponses: 9
    Dernier message: 18/07/2008, 10h03
  4. Segfault sur contructeur de string
    Par lemmel dans le forum SL & STL
    Réponses: 7
    Dernier message: 02/09/2007, 13h50
  5. Probleme sur un sprintf
    Par nerixm dans le forum Débuter
    Réponses: 11
    Dernier message: 27/08/2007, 15h33

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