ESIEE 2011 2012 IN3T01 Programmation C TP 3 corrigé exercices rédigés et corrigés par J.C Georges et Michel Landschoot http://www.esiee.fr/~landschm/in3t01/ Objectifs: réussir ses premiers pas sur les chaînes de caractères en langage C, Notions abordées: pointeurs, chaînes de caractères, fonctions avec un pointeur de caractères en paramètre Écrivez un programme pour tester toutes les fonctions programmées dans les exercices suivants. Déclarations prototypes des fonctions: fichier chaine.h. Définition des fonctions: fichier source chaine.c. Programme de test avec les appels des fonctions sur les chaînes: fichier source testchaine.c Penser à inclure chaine.h dans les 2 fichiers sources. Directives de compilation: gcc -c chaine.c gcc -c testchaine.c gcc chaine.o testchaine.o -o testchaine ==> génération du binaire chaine.o ==> génération du binaire testchaine.o ==> édition de liens et génération de l'exécutable testchaine. NOTATION TABLEAU fichier chaine.h #ifndef CHAINE_H #define CHAINE_H int chaine_vers_entier (char * s); char * miroir (char * s); int debute_par (char * chaine1, char * chaine2); int presence (char * chaine1, char * chaine2); int compte (char c, char * s); int compte2 (char c, char * s); char * cherche_remplace (char c, char r, char * s); #endif
fichier chaine.c #include <stdio.h> #include "chaine.h" Exercice 1: Conversion chaîne de caractères en entier Écrivez une fonction qui prend en argument une chaîne de caractères représentant un entier en décimal et retourne l entier équivalent ( "123"! 123). On supposera que la chaîne est correcte et représente bien un entier. Prototype : int chaine_vers_entier (char * s); Une chaîne représentant un entier est composée d'une suite de caractères chiffres (compris entre '0' et '9'), éventuellement précédée d'une caractère '-' ou '+'. L'entier correspondant à un caractère chiffre est égal à ce caractère moins le caractère '0'. ('2 '- '0' ==> 2) S'il y a un signe, on le mémorise sous la forme d'une variable valant 1 ou +1. Puis, on accumule dans un résultat le nombre représenté selon le principe suivant : pour chaque chiffre, on multiplie par 10 la valeur accumulée et on ajoute la valeur du chiffre. En fin de calcul, on retourne le résultat multiplé par le signe. int chaine_vers_entier (char * s) int i = 0,signe = 1, res = 0; if (s[i] == '+') ++i; else if (s[i] == '-') signe = -1; ++i; for ( ; s[i]!= '\0'; ++i ) res = res * 10 + (s[i] - '0'); return signe * res;
Exercice 2: Miroir Écrivez une fonction qui prend en argument une chaîne de caractères, la renverse sur elle-même et retourne l adresse de cette chaîne. Exemple: le miroir de "toto " est "otot". Prototype : char * miroir (char * s); Il faut déjà parcourir la chaîne pour accéder à son dernier caractère. Puis permuter les couples de caractères symétriques en évitant de le faire deux fois, ce qui laisserait la chaîne inchangée. char * miroir (char * s) int g, d; /* indices gauche et droit de parcours */ char c; for (d = 0; s[d]!= '\0'; ++d); for (g = 0, --d; g < d ; ++g, --d) c = s[g]; s[g] = s[d]; s[d] = c; return s;
Exercice 3: Recherche de motif (1) Écrivez une fonction qui prend en argument deux chaînes de caractères et retourne 1 si la première chaîne commence par la seconde et 0 sinon. Prototype : int debute_par (char * chaine1, char * chaine2); Il sufft de regarder pour chaque caractère de chaine2 s'il est égal au caractère correspondant de chaine1. Dès qu'il y a inégalité, on retourne 0 (faux). Si on arrive en bout de chaine2 sans avoir détecté d'inégalité, on retourne 1 (vrai). Note : ce programme renvoie toujours 1 si la chaine2 est vide. int debute_par (char * chaine1, char * chaine2) int i; for (i = 0; chaine2[i ]!= '\0'; ++i) if ( chaine1[i]!= chaine2[i]) return 0; return 1;
Exercice 4: Recherche de motif (2) Écrivez une fonction qui prend en argument deux chaînes de caractères et retourne la position de la première occurrence de la chaîne2 dans la chaîne1 si elle y est présente et 1 sinon. Prototype : int presence (char * chaine1, char * chaine2); Un appel itératif à debute_par permettra de détecter la présence de la sous-chaîne. Note : ce programme considère que la chaîne vide débute toute chaîne (retour 0). int presence (char * chaine1, char * chaine2) int i; for (i = 0; chaine1[i ]!= '\0'; ++i) if (debute_par(&chaine1[i], chaine2)) return i; return -1; Exercice 5: Fréquence Écrivez une fonction qui compte le nombre d occurrences d un caractère c dans une chaîne s. La fonction pourra être récursive. Écrivez un programme pour tester cette fonction. Prototype : int compte (char c, char * s); C'est encore un accumulateur : int compte (char c, char * s) int i, acc = 0; for (i = 0; s[i]!= '\0'; ++i) if (s[i] == c) ++acc; return acc;
Une fonction récursive peut être écrite selon le principe suivant : int compte2 (char c, char * s) if (*s == '\0') /* s [0] == '\0' */ return 0; return ((*s == c )? 1 : 0) + compte2 (c,s + 1); Exercice 6:. Chercher/remplacer Écrivez une fonction qui recherche dans une chaîne chaque caractère c pour le remplacer par un caractère r et retourne l adresse de la chaîne. Prototype : char * cherche_remplace (char c, char r, char * s); C'est un parcours simple avec remplacement lorsque l'on tombe sur le caractère à remplacer. char * cherche_remplace (char c, char r, char * s) int i; for (i = 0; s[i]!= '\0'; ++i) if (s[i] == c) s[i] = r; return s; fichier testchaine.c #include <stdio.h> #include "chaine.h" int main(void) int chaineversentier; char tab[] = "In girum imus nocte et consumimur igni: gag eht"; int debutepar, present, compteur, compteur2;
chaineversentier = chaine_vers_entier ("757"); printf("chaine: 757 convertie en nombre %d \n", chaineversentier); printf("chaine: %s \n", tab); // retour de miroir non utilisé cf strcpy... miroir (tab); printf("chaine inversée: %s \n", tab); // on se remet dans le bon sens de lecture! */ miroir(tab); debutepar = debute_par (tab, "In girum"); if (debutepar == 1) printf("%s débute par In girum \n", tab); else printf("%s ne débute pas par In girum \n", tab); present = presence (tab, "nocte"); if (present!= -1) printf("%s contient nocte \n", tab); else printf("%s ne contient pas nocte \n", tab); compteur = compte ('e', tab); printf("%d e dans %s \n", compteur, tab); compteur2 = compte2 ('e', tab); printf("%d e dans %s \n", compteur2, tab); // retour de cherche_remplace non utilisé cf strcpy... printf("chaine: %s \n", tab); printf("==> e à remplacer par E \n"); cherche_remplace ('e', 'E', tab); printf("chaine: %s \n", tab); return 0;
NOTATION POINTEUR Les algorithmes sont les mêmes que ceux de la notation tableau. L'arithmétique des pointeurs est mise en oeuvre. fichier chainenotationpointeur.h #ifndef CHAINE_NOTATIONPOINTEUR_H #define CHAINE_NOTATIONPOINTEUR_H int chaine_vers_entier_pointeur (char * s); char * miroir_pointeur (char * s); int debute_par_pointeur (char * chaine1, char * chaine2); int presence_pointeur (char * chaine1, char * chaine2); int compte_pointeur (char c, char * s); int compte2_pointeur (char c, char * s); char * cherche_remplace_pointeur (char c, char r, char * s); #endif fichier chainenotationpointeur.c #include <stdio.h> #include "chainenotationpointeur.h" int chaine_vers_entier_pointeur (char * s) int signe = 1, res = 0; if (*s == '+') ++s; else if (*s == '-') signe = -1; ++s;
for ( ; *s!= '\0'; ++s ) res = res * 10 + (*s - '0'); return signe * res; char * miroir_pointeur (char * s) char * g, * d; /* pointeurs gauche et droit de parcours */ char c; for (d = s; *d!= '\0'; ++d); for (g = s, --d; g < d ; ++g, --d) c = *g; *g = *d; *d = c; return s; int debute_par_pointeur (char * chaine1, char * chaine2) while(*chaine2!= '\0') if ( *chaine1!= *chaine2) return 0; chaine1++; chaine2++; return 1;
int presence_pointeur (char * chaine1, char * chaine2) while (*chaine1!= '\0') if (debute_par_pointeur(chaine1, chaine2)) return 1; chaine1++; return -1; int compte_pointeur (char c, char * s) int acc = 0; while (*s!= '\0') if ( *s == c) ++acc; s++; return acc; int compte2_pointeur (char c, char * s) if (*s == '\0') /* s [0] == '\0' */ return 0; return ((*s == c )? 1 : 0) + compte2_pointeur (c,s + 1); char * cherche_remplace_pointeur (char c, char r, char * s) int i; for (i = 0; s[i]!= '\0'; ++i) if (s[i] == c) s[i] = r;
return s; fichier testchainenotationpointeur.h #include <stdio.h> #include "chainenotationpointeur.h" int main(void) int chaineversentier; char tab[] = "In girum imus nocte et consumimur igni: gag eht"; int debutepar, present, compteur, compteur2; chaineversentier = chaine_vers_entier_pointeur ("757"); printf("chaine: 757 convertie en nombre %d \n", chaineversentier); printf("chaine: %s \n", tab); // retour de miroir non utilisé cf strcpy... miroir_pointeur(tab); printf("chaine inversée: %s \n", tab); // on se remet dans le bon sens de lecture! */ miroir(tab); debutepar = debute_par_pointeur(tab, "In girum"); if (debutepar == 1) printf("%s débute par In girum \n", tab); else printf("%s ne débute pas par In girum \n", tab); present = presence_pointeur (tab, "nocte"); if (present!= -1) printf("%s contient nocte \n", tab); else printf("%s ne contient pas nocte \n", tab);
compteur = compte_pointeur ('e', tab); printf("%d e dans %s \n", compteur, tab); compteur2 = compte2_pointeur ('e', tab); printf("%d e dans %s \n", compteur2, tab); // retour de cherche_remplace non utilisé cf strcpy... printf("chaine: %s \n", tab); printf("==> e à remplacer par E \n"); cherche_remplace_pointeur ('e', 'E', tab); printf("chaine: %s \n", tab); return 0;