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 :

Cloner une chaîne de caractères


Sujet :

C

  1. #1
    Membre actif
    Femme Profil pro
    Étudiant
    Inscrit en
    Juin 2015
    Messages
    22
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 30
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2015
    Messages : 22
    Par défaut Cloner une chaîne de caractères
    Bonjour,
    mon enseignant m'a donné le corrigé de l'exercice ci-dessous, mon problème est que je ne comprends pas
    -la signification de "fprintf", "stderr", pouvez m'expliquer? Et y a t-il une différence entre printf et fprintf?
    -Quelle est la signification de cette ligne char * r = (char*) malloc((strlen(src)+1) * sizeof(char))? Quel est l'intéret de l'utiliser?
    -Comment utilise t-on les * dans cet exercice? je sais juste qu'on utilise les astérisques * pour les pointeurs .En fait je débute en c, j'ai du mal à comprendre concrètement l'utilisation des pointeurs, des *,... quelqu'un pourrait t-il m'expliquer?
    Merci d'avance

    Exercice : Cloner une chaîne de caractères
    On souhaite écrire une fonction cloneStrings qui retourne une copie d'une chaîne de caractèrespassee en paramètre.

    corrigé:
    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
    char * cloneString(char * src)
    {
        if(src == NULL)
        {
            fprintf(stderr,"Entr�e vide!\n");
            exit(1);
        }
     
        char * r = (char*) malloc((strlen(src)+1) * sizeof(char));
        if(r == NULL)
        {
            fprintf(stderr,"Plus assez de m�moire!\n");
            exit(1);
        }
     
        /** sauvegarde de l'adresse du d�but pour le retour */
        char * t = r;
     
        while ((*r = *src) != '\0')
        {
            src++;
            r++;
        }
     
        return t;
    }

  2. #2
    Expert confirmé Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 041
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 041
    Par défaut
    salut,

    Citation Envoyé par couturierclaire Voir le message
    -la signification de "fprintf", "stderr", pouvez m'expliquer? Et y a t-il une différence entre printf et fprintf?
    stderr est le descripteur de la sortie d'erreur standard, fprintf est une fonction de la bibliothèque C qui permet d'afficher du texte dans un descripteur (fichier, socket, sortie standard) spécifique
    la fonction printf affiche du texte sur la sortie standard (stdout) et donc est l'équivalent d'un appel à fprintf(stdout, ...)
    toutes ces questions trouvent facilement leurs réponses en allant lire les manpages concernées, elles existent y compris en français par ailleurs

    -Quelle est la signification de cette ligne char * r = (char*) malloc((strlen(src)+1) * sizeof(char))? Quel est l'intéret de l'utiliser?
    c'est la déclaration d'un pointeur r et on accroche à ce pointeur dans la foulée une zone mémoire dans le tas (heap) qu'on alloue grâce à malloc
    l'intérêt de l'utiliser... ben c'est de pouvoir réserver une zone mémoire dont on va pouvoir disposer dans le programme, et en l'occurrence de faire une allocation dite dynamique de mémoire, c'est à dire qu'on ne connait pas la taille à l'avance et on alloue la quantité de mémoire dont on a besoin pendant l'exécution du programme plutôt qu'en précisant une taille fixe au compilateur comme char r[42] par exemple

    -Comment utilise t-on les * dans cet exercice? je sais juste qu'on utilise les astérisques * pour les pointeurs .En fait je débute en c, j'ai du mal à comprendre concrètement l'utilisation des pointeurs, des *,... quelqu'un pourrait t-il m'expliquer?
    j'ai un peu de mal à comprendre je t'avouerai, soit la pédagogie de l'enseignant est étrange, lâcher ses élèves comme ça sans qu'ils ne connaissent absolument rien à rien, soit le but est que les élèves cherchent et comprennent par eux même et auquel cas tu ne joues pas tellement le jeu, non ?
    je vais pas te faire un cours sur les pointeurs en C, par ailleurs il y a énormément de tutoriels qui existent à ce sujet à commencer par ceux qu'on peut trouver ici même sur dvp

  3. #3
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 835
    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 835
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par couturierclaire Voir le message
    Comment utilise t-on les * dans cet exercice? je sais juste qu'on utilise les astérisques * pour les pointeurs .En fait je débute en c, j'ai du mal à comprendre concrètement l'utilisation des pointeurs, des *,... quelqu'un pourrait t-il m'expliquer?
    Bonjour

    D'accord avec BufferBob. T'envoyer un corrigé dans la tronche sans te donner les bases pour le comprendre est anti-pédagogique.

    Pour comprendre les pointeurs, il suffit de se souvenir que de deux choses:
    1. toute variable possède une adresse (valeur numérique de son emplacement dans la mémoire)
    2. cette adresse peut être manipulée mais uniquement dans une variable appropriée: un pointeur


    Exemple simple: tu déclares un double: double pi=3.14159. Cette variable "pi" se trouve alors positionnée à l'adresse (par exemple) 0x1000. Hé bien tu as tout à fait le droit de récupérer ce 0x1000 dans une autre variable.
    La principale difficulté sera la notation de cette variable. Tu pourrais penser que 0x1000 étant entier on peut écrire int adresse=0x1000. En théorie c'est possible mais il y a un second point à prendre en considération: il faut que le compilateur sache qu'à l'adresse 0x1000 il y a un double.
    Pour ça, la seule façon est d'écrire alors double *adresse=0x1000. Ca signifie que
    • adresse est une variable contenant l'adresse d'un double
    • étoile adresse (ce qui est pointé par la variable "adresse") contient un double (ne pas oublier le mot "étoile" quand on parle d'un pointeur)


    A partir de là, tu peux t'amuser à afficher "pi" ou "étoile adresse", les deux référencent la même chose. Et de même modifier l'un ou l'autre modifient la même chose (le contenu de l'emplacement mémoire 0x1000). Petite parenthèse: la valeur "0x1000" est donnée comme exemple. En réalité, cette adresse n'est pas connue mais peut-être demandée par l'opérateur "&" => double *adresse=&pi.

    Ce qui donne au final:
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    double pi=4.00;
    double *adresse=π
    printf("Mes valeurs sont %f et %f\n", pi, *adresse);
    pi=3.14;
    printf("Mes valeurs sont %f et %f\n", pi, *adresse);
    *adresse=3.1416;
    printf("Mes valeurs sont %f et %f\n", pi, *adresse);

    A quoi ça sert ? Principalement dans 2 situations les plus fréquentes
    1. Quand une fonction doit modifier une variable qui lui est passée depuis l'extèrieur. En effet, comme une fonction ne reçoit qu'une copie de ce qu'on lui passe, si on lui passe une variable la fonction copiera son contenu dans sa zone de travail. Et si elle modifie ce contenu, elle ne modifiera qu'une copie. Donc, solution, on lui passe l'adresse de la variable à modifier. La fonction recopie cette adresse dans sa zone de travail (un pointeur évidemment) mais comme l'espace mémoire d'un programme est unique, cette adresse même copiée reste celle de la variable en question. Ainsi la fonction, en modifiant "ce qui est pointé par", modifiera la variable réelle

      Exemple
      Code c : 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
      void modif(int *pt    /* La fonction recevra une adresse et la stockera dans un pointeur approprié */)
      {
          // Incrément de la variable pointée
          (*pt)=(*pt)+1;
      }
       
      int main()
      {
          int valeur=10;
          int i;
          for (i=0; i < 10; i++)
          {
              modif(&valeur);    // La fonction attendant une adresse, on lui passe forcément une adresse ; cette adresse étant ici l'adresse de la variable
              printf("Valeur vaut %d\n", valeur);
          }
      }
    2. Quand on veut accélérer le balayage d'un tableau. En effet, l'accès à un élément [x] d'un tableau implique pour le compilo de se placer au début du tableau et de décaler de x positions. Si on répète plusieurs fois ce tab[x] dans les instructions, cette opération se fera alors autant de fois que c'est demandé. Certains intervenants sont persuadés qu'aujourd'hui les compilateurs savent reconnaitre ces instructions et les optimiser mais personnellement je pense qu'on n'est jamais si bien servi que par soi-même (surtout si on commence à avoir des tableaux de structures...)
      Donc pour éviter ce "décalage", il suffit de placer un pointeur au début du tableau (l'adresse de son premier élément) et ensuite d'incrémenter le pointeur. Comme les éléments d'un tableau se suivent toujours en mémoire, et comme aussi l'incrément d'un pointeur de "1" incrémente en réalité de "1 * taille du type" (car les différents types possèdent différentes tailles en mémoire), le pointeur passe alors d'élément en élément en suivant ceux du tableau. C'est en fait ce que fait ton prof quand il balaye ta chaine (une chaine n'étant qu'un tableau de caractères).
      Et si en plus on peut insérer dans le tableau une valeur "spéciale" (valeur qui n'est pas utilisée par ton algo), alors tu peux utiliser cette valeur pour "détecter" la fin de ton tableau sans avoir à connaitre (ni créer de variable pour la mémoriser) la taille de ton tableau. C'est ce qui se passe avec les chaines (suite de caractère terminée par un '\0').

      Exemple
      Code c : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      int main()
      {
          int positifs[]={1, 3, 5, 7, 9, 11, 13, -1};   // Tableau des positifs - La taille n'est pas définie, le compilo la calculera lui-même en fonction des éléments qui sont dedans - A noter le "-1" ne faisant pas partie des positifs
          int *pt;                                             // Un pointeur sur un entier
       
          for (pt=positifs; *pt != 0; pt++)     // pt se place sur l'adresse du premier élément (le nom d'un tableau correspond à l'adresse du premier), on teste tant que l'élément pointé n'est pas égal à -1 et on incrémente le pointeur
              printf("Mon élément vaut [%d]\n", *pt);
      }

    Si ensuite tu veux rajouter d'autres nombres, tu les rajoutes simplement avant le "-1" et tu recompiles. Ton code s'adaptera tout seul à ce nouveau tableau (écrire un code c'est bien mais l'écrire de façon à faciliter ses évolutions c'est mieux).

    C'est la base des pointeurs. Ensuite on peut en faire de grandes choses (comme stocker une suite de fonctions dans un tableau de "pointeurs de fonctions" puis, ensuite, les faire exécuter une après l'autre par une simple boucle...)

    En espérant t'avoir aidé...
    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]

  4. #4
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    18 258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 18 258
    Par défaut
    La notion de pointeur n'est pas simple à comprendre.

    Pour compléter l'excellente réponse de Sver, et ayant eu (et ayant encore) des difficultés avec les pointeurs :

    une variable en C est stockée à une adresse mémoire réservée par le compilateur. Personnellement tu te fous de cette adresse pour de la programmation simple, on peut en avoir besoin dans des cas bien précis (notamment la présentation de sver) mais pas tout le temps.

    Un pointeur est en fait une variable spéciale pointant sur une autre variable. Mais le pointeur et la variable pointée représente la même valeur

    exemple ( je reprends l'exemple de Sver) : Ton compilateur fixe ta variable pi à l'adresse 0x1000, elle va contenir la valeur 3.14159
    Tu crée un pointeur sur cette variable : adresse (double devant signifiant bien entendu variable/pointeur de type double (réel double précision)).
    Cette variable sera à l'adresse 0x1200 (fixée par le compilateur- pas par nous comme pour 0x1000 d'ailleurs) par exemple et contiendra comme valeur 0x1000 (l'adresse mémoire de ce qu'il doit pointer). Le compilateur va comprendre que quand tu fais *adresse=5; il doit affecter 5 non pas à l'adresse 0x1200, mais à l'adresse que le pointeur pointe : 0x1000 et donc remplacer 3.14159 par 5, ce qu'il aurait aussi fait avec pi=5; Dans certains cas (cf post de sver) tu es obligé de passer par un pointeur.

    *adresse2=&0x1000 va affecter l'adresse 0x1000 au pointeur adresse2. pi, adresse, et adresse2 correspondront à la même chose. Dans notre cas nous connaissons l'adresse, mais mettre n'importe quoi à la place de 0x1000 va générer un plantage (écrasement d'une donnée à une adresse affectée à qq chose mais seul le programme sait quoi) ou un SEGFAULT (erreur de segmentation : tentative d'accès à une zone mémoire n'appartenant pas au programme et c'est interdit).

    Le cas des chaines en C :

    En C il n'existe pas de chaine de caractères. Une chaine sera vue comme un tableau de caractères terminée par un 0.

    solution la plus simple : chaine char[25];

    Dans ce cas, ta chaine sera limitée à 24 caractères (25 - 1 réservé pour le0 de fin)

    solution avec pointeur : chaine *char;
    Ceci suffit à déclarer le pointeur mais n'est surtout pas faire tout seul.
    il faut au moins
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    chaine *char;
    chaine=NULL;
    Ceci va signifier que ton pointeur ne pointe sur rien. Car le pointeur étant une adresse mémoire, lors de sa création il réserve une adresse mais on ne sait pas ce qu'il y a comme valeur à ce moment (il n'y a pas de mise à 0). NULL signifie rien, en code machine il mettra probablement 0 à l'adresse
    mémoire dans le cas d'une affectation ou dans ton exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    if(src == NULL)
    {
    fprintf(stderr,"Entr�e vide!\n");
    exit(1);
    }
    si src est NULL (donc affecté à rien) il quittera après affichage du message, pour ne pas cloner n'importe quoi

    Si tu veux stocker une chaine que tu vas lire au clavier par exemple, il te faudra réserver de la place avec la fonction malloc (tu l'as dans ton corrigé). si tu fais un malloc(500). Tu auras 499 caractères possible pour ta chaine(ne pas oublier le 0 de fin. Lors de la lecture, il te faudra utiliser les fonctions C à bon escient pour ne pas dépasser la capacité de ta chaine.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    char * r = (char*) malloc((strlen(src)+1) * sizeof(char));
    Dans cet exemple le (char*) devant malloc est inutile, c'est un CAST qui permet de convertir un type en un autre (exemple int en char) pour le compilateur ne bloque pas si on est pas sensé le faire. comme tu le vois malloc prend la longueur de src (strlen(src)) +1 pour le caractère 0. le * sizeof(char) est inutile si la longueur d'un char est à 1 car tu feras une multiplication par 1. Il me semble que char est normalisé à 1 octet mais je suis pas sûr.

    Ce que fait ton code :
    - Il vérifie que la chaine passée en paramètre n'est pas nulle (sinon il quitte)
    - il alloue de la mémoire (taille src1+1) et quitte si l'allocation échoue (en cas d’échec de malloc, son retour sera NULL cf man malloc)
    - copie de chaque caractère à l'adresse du pointeur jusqu'à atteinte de 0 dans la chaine source. Incrément des pointeurs

    le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    while ((*r = *src) != '\0')
    {
    src++;
    r++;
    }
    aurait pu être écrit comme ceci de façon plus compréhensible :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    while(*src=!'0')
    {
      *r=*src;
      ++src;
      ++r;
    }
    Ce code est équivalent à strdup
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

Discussions similaires

  1. Cloner une chaîne de caractères
    Par mrcode dans le forum C
    Réponses: 5
    Dernier message: 12/06/2015, 10h40
  2. Réponses: 8
    Dernier message: 12/02/2013, 01h08
  3. [Debutant(e)] Analyse d'une chaîne de caractères
    Par maire106 dans le forum Langage
    Réponses: 6
    Dernier message: 22/03/2004, 15h04
  4. Inverser une chaîne de caractères
    Par DBBB dans le forum Assembleur
    Réponses: 2
    Dernier message: 30/03/2003, 11h09
  5. Réponses: 3
    Dernier message: 09/05/2002, 01h39

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