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 :

Petit programme élémentaire


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4
    Par défaut Petit programme élémentaire
    Bonjour,

    Je me mets au C sous MacOs X. J'ai écrit le petit programme suivant :

    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
    #include <stdio.h>
     
    main()
    {
      int y,z;
      char *s;
      printf("Bonjour, ceci est mon premier programme.\n");
      printf("Entrez deux nombres entiers separes par un espace : ");
      scanf("%d %d",&y,&z);
      printf("Le premier entier que vous avez entre est %d. \n",y);
      printf("Le deuxieme entier que vous avez entre est %d. \n",z);
      printf("La somme de ces entiers est %d. \n",y+z);
      printf("Entrez une chaîne de caractères : ");
      scanf("%s",&s);
      printf("Vous avez entre : ");
      puts(s);
      printf("\n");
    }
    La compilation se déroule sans problème, mais l'exécution de l'instruction "puts" produit une Segmentation fault. Pourquoi ?

    Merci d'avance de m'aider...
    jydegos

  2. #2
    Membre émérite
    Avatar de Pouet_forever
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    671
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 671
    Par défaut
    La déclaration de 's' ne concorde pas avec son utilisation. Soi tu alloues dynamiquement la mémoire, soit tu le déclare en tant que tableau. Le scanf est faux aussi, il ne faut pas envoyer l'adresse de ce pointeur mais bien le pointeur lui-même.

    On préfèrera la syntaxe 'int main(void)' pour le main.
    La fonction main DOIT retourner quelque chose, 0 ou EXIT_SUCCESS pour une terminaison correcte, une autre valeur (dépendante de l'environnement) pour dire qu'il y a eu une anomalie.
    Autre chose, il y a des balises de code (le petit # au dessus du cadre de saisie de texte) utilises les.

    @+

  3. #3
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 104
    Par défaut
    Salut

    Il y a deux problèmes majeurs :

    Là, scanf va écraser une partie de la pile.

    Un pointeur est un objet situé quelque part en mémoire (ici dans la pile). Et sa valeur est une adresse... vers laquelle on peut pointer (c'est tout l'intérêt d'un pointeur).

    La fonction scanf a besoin de connaitre l'adresse où elle va écrire les données.

    Or, là, tu lui envoies non pas l'adresse pointée par le pointeur, mais l'adresse du pointeur ! Du coup, elle va écraser une partie de la pile, c'est-à-dire que tu vas modifier s (qui pointera encore n'importe où), ainsi que d'autres variables.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    char *s; (1)
    ...
    scanf("%s",&s); (2)
    ...
    puts(s); (3)
    1) s n'est pas initialisé : il pointe n'importe où.
    2) La mauvaise utilisation de scanf ici modifie la valeur de s... qui continuera encore à pointer n'importe où.
    3) Tu veux afficher une chaine situé à une adresse bidon (i.e. : interdite)... Ca ne peut que crasher.

  4. #4
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4
    Par défaut
    Citation Envoyé par jeroman Voir le message
    Salut

    Il y a deux problèmes majeurs :

    Là, scanf va écraser une partie de la pile.

    Un pointeur est un objet situé quelque part en mémoire (ici dans la pile). Et sa valeur est une adresse... vers laquelle on peut pointer (c'est tout l'intérêt d'un pointeur).

    La fonction scanf a besoin de connaitre l'adresse où elle va écrire les données.

    Or, là, tu lui envoies non pas l'adresse pointée par le pointeur, mais l'adresse du pointeur ! Du coup, elle va écraser une partie de la pile, c'est-à-dire que tu vas modifier s (qui pointera encore n'importe où), ainsi que d'autres variables.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    char *s; (1)
    ...
    scanf("%s",&s); (2)
    ...
    puts(s); (3)
    1) s n'est pas initialisé : il pointe n'importe où.
    2) La mauvaise utilisation de scanf ici modifie la valeur de s... qui continuera encore à pointer n'importe où.
    3) Tu veux afficher une chaine situé à une adresse bidon (i.e. : interdite)... Ca ne peut que crasher.
    J'ai remplacé scanf("%s",&s); par scanf("%s",s); changé le prototype de la fonction main, et ajouter un return 0; symbolique à la fin du main. Il n'y a pas d'erreur de compilation avec l'option gdb. Cette fois j'obtiens "Bus error" à l'exécution.

    Nouveau code :

    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
     
    #include <stdio.h>
    #include <stdlib.h>
     
    int main(void)
    {
      int y,z;
      char *s;
      printf("Bonjour, ceci est mon premier programme.\n");
      printf("Entrez deux nombres entiers separes par un espace : ");
      scanf("%d %d",&y,&z);
      printf("Le premier entier que vous avez entre est %d. \n",y);
      printf("Le deuxieme entier que vous avez entre est %d. \n",z);
      printf("La somme de ces entiers est %d. \n",y+z);
      printf("Entrez une chaîne de caractères : ");
      scanf("%s",s);
      printf("Bonjour. Jusqu'ici, pas d'erreur");
      printf("Vous avez entre : ");
      puts(s);
      printf("\n");
      return 0;
    }
    Je ne veux bien initialiser s, mais à quoi ???

    Merci encore...

  5. #5
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 104
    Par défaut
    Je ne veux bien initialiser s, mais à quoi ???
    Il faut bien écrire la chaine quelque part en mémoire, à une adresse valide et connue. Pas n'importe où.

    Il faut donc créer un tableau :

    * soit statique :
    * soit dynamique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    char * s = NULL;
    ...
    s = malloc( ... );
    if (s != NULL)
    {
    ...
    free(s) , s = NULL;
    }
    ...
    NULL est une valeur spéciale, qui indique que l'adresse n'est pas valide.
    Il est recommandé (voire capital dans certains programmes) d'initialiser les pointeurs avec cette valeur.
    Pour ton programme, qui est un programme très simple, ce n'est pas capital de le faire, mais ça permet de garder les bonnes habitudes de programmation. De même, ici, il n'est pas essentiel de remettre le pointeur à NULL après le free.

    Car un pointeur non initialisé peut provoquer des bugs dans certains programmes : en effet, si le pointeur a une valeur bidon mais pas NULL, comment savoir si cette valeur correspond bien à une adresse retournée par malloc ? comment savoir on peut l'utiliser pour lire ou écrire ? comment savoir si on peut l'utiliser avec free ? En initialisant à NULL, il n'y a donc plus de doutes. Et remettre à NULL après un free indique par la suite que le pointeur ne pointe plus à une adresse valide.

  6. #6
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 495
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 495
    Par défaut
    Il y a un principe en C que tu peux pratiquement considérer comme fondamental : c'est toi qui gères la mémoire !

    Si tu fais un scanf, tu demandes au système de te lire une chaîne de caractères… qu'il va bien falloir mettre quelque part. C'est un peu comme quand tu te fais livrer un canapé. Le transporteur veut bien te l'amener jusque chez toi mais il t'appartient quand même de lui faire de la place.

    Ton pointeur « s » est donc là pour cela. Il est censé contenir l'adresse en mémoire d'un emplacement quelconque, en l'occurrence de la place pour déposer notre chaîne. C'est donc à toi d'initialiser ce pointeur en y mettant une adresse valide.

    Maintenant, pour réserver de la place en mémoire et connaître son emplacement, il y a deux solutions :

    • Soit tu déclares un tableau, comme si tu déclarais une variable locale ordinaire. Le compilateur va alors réserver cette place directement dans la pile, ce qui est acceptable à condition que ce tableau ne soit pas trop gros et que tu le libères rapidement. Comme la taille et l'emplacement de ce tableau sont, par définition, connus à la compilation, tu peux passer directement le nom de ce tableau à scanf(). Un nom de tableau utilisé seul correspond justement à son emplacement, et plus précisément à l'adresse où il commence. Il s'agit donc d'une expression de type « pointeur sur [le-type-de-données-qu'il-contient] » ;
    • Soit tu utilises malloc() (Memory Allocation) pour demander au système — et à l'exécution — de t'en réserver un peu, à l'emplacement de son choix. La fonction malloc() te renverra donc un pointeur qu'il te faudra enregistrer quelque part : dans « s ». Éventuellement, malloc() peut te renvoyer « NULL » si elle n'a pas réussi à allouer la place demandée (soit parce que c'est trop grand, soit parce qu'il n'y a plus de place). Il t'appartient donc de vérifier si « s » est bien différent de « NULL » avant de la passer à scanf().

  7. #7
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4
    Par défaut
    Bon... je crois que je ne suis pas encore mûr pour les chaînes de tailles variables.

    Donc j'ai essayé le code suivant :

    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
     
    #include <stdio.h>
    #include <stdlib.h>
     
    int main(void)
    {
      char s[255]="Ceci est un exemple de chaîne de caractères très longue";
      printf("Bonjour, ceci est mon premier programme.\n");
      puts(s); /* (1) */
      printf("Entrez une chaîne de caractères : ");
      scanf("%s",s);
      printf("Bonjour. Jusqu'ici, pas d'erreur.\n");
      printf("Vous avez entré : ");
      puts(s); /* (2) */
      printf("\n");
      return 0;
    }
    Il n'y a plus d'erreur d'exécution mais le résultat est inattendu : l'instruction (1) s'exécute correctement, tandis que l'instruction (2) n'affiche que la partie précédent le premier espace de ce que j'ai saisi ?!?!

  8. #8
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 104
    Par défaut
    tandis que l'instruction (2) n'affiche que la partie précédent le premier espace de ce que j'ai saisi ?!?!
    C'est le but de la fonction. scanf analyse l'entrée en s'appuyant sur le format donné.
    Si tu lui donnes "%s", la fonction lit l'entrée standard et remplit le buffer.

    Un cours complet au sujet de scanf : http://xrenault.developpez.com/tutoriels/c/scanf/

Discussions similaires

  1. petit programme
    Par vmitz73 dans le forum C++
    Réponses: 4
    Dernier message: 07/12/2005, 11h05
  2. [LG] Recherche de petits programmes Pascal
    Par Sid ali dans le forum Langage
    Réponses: 1
    Dernier message: 24/11/2005, 14h03
  3. aide petit programme pour débutant
    Par kartp0rqx dans le forum C
    Réponses: 16
    Dernier message: 14/10/2005, 19h31
  4. Faisabilité d'un petit programme FTP...
    Par ptit_seb dans le forum Windows
    Réponses: 2
    Dernier message: 15/09/2005, 21h10
  5. [SRC] Petit programme avec BD
    Par Nico62 dans le forum C++Builder
    Réponses: 3
    Dernier message: 10/01/2005, 20h07

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