Vous êtes sur la page 1sur 13

ESIEE IGI-3008 TP Langage C no 2 (corrig) 2014-2015

Tableaux et pointeurs (corrig)

Tous les exercices sont faire.

1 Tableaux

Exercice 1. Moyenne (*)


crivez une fonction qui calcule la moyenne de n nombres stocks dans un tableau de double .
Prototype : double moyenne (double t[], int n);
Corrig
***************************************************************************
Le principe est de parcourir le tableau pour en calculer la somme de tous ses lments, puis de
retourner le quotient de cette somme par le nombre dlments.
Dans une boucle, on accumulera dans une variable les valeurs des lments de calcule de la somme.
Ce genre de boucle est appel schma daccumulation Par exemple, lcriture mathmatique
b
X
expr(i)
i=a

scrira en C :
var=0.0;
for (i=a;i<=b;++i)
var += expr(i);
Le parcours dun tableau de n lments en langage C se fait en gnral par une boucle allant
de lindice 0 lindice n 1 (inclus). Il vaut mieux crire la condition de rptition sous la forme
i < n que sous la forme i <= n-1 , ce qui permet dune part de gagner une soustraction par tour
de boucle, et dautre part de garder sous forme explicite le nombre dlments traiter dans lcriture
du programme.
Ce qui donne la solution possible suivante :
double moyenne(double *t, int n) {
int i;
double total=0.0;
for (i = 0 ; i < n ; ++i) {
total += t[i];
}
return total/n;
}
***************************************************************************
Exercice 2. Recherche dlments sur critre(*)
crivez la fonction qui retourne lindice du premier lment strictement ngatif parmi les n
premiers lments dun tableau de double ( -1 si aucun lment nest ngatif).
Prototype : int indice_premier_negatif (double t[], int n);
Corrig
***************************************************************************
Ici, lanalyse du problme nous conduit la solution suivante :

1/13
ESIEE IGI-3008 TP Langage C no 2 (corrig) 2014-2015

on parcourt le tableau
si lon rencontre un lment ngatif, on termine la fonction en retournant lindice courant
si lon a parcouru lintgralit du tableau sans avoir rencontr dlment ngatif, on retourne
1 comme indiqu dans la spcification.
Attention : cest aprs la boucle que lon peut constater labsence dlment ngatif. Une erreur
courante de programmation consiste retourner 1 trop tt, avec un test du style
if (t[i]<0) return i; else return -1;
ce qui conduit retourner -1 si le premier un lment nest pas ngatif. Cest toute la diffrence
entre une recherche existentielle : i t[i] < 0 et une recherche universelle i t[i] 0.
int indice_premier_negatif (double t[], int n) {
int i;
for (i=0; i<n ; ++i) {
if (t[i]<0)
return i;
}
return -1;
}
***************************************************************************
Exercice 3. Maximum (*)
crivez la fonction qui retourne la valeur du plus grand des n premiers lments dun tableau
de double .
Prototype : double valeur_plus_grand (double t[], int n);

Corrig
***************************************************************************
Un parcours du tableau simpose, avec une mmorisation du plus grand lment rencontr. En
effet, le plus grand lment du tableau peut se situer nimporte o (en dbut, en cours ou en fin de
tableau) et donc on ne peut le connatre quaprs avoir examin tous les lments du tableau, et le
mmoriser permettra de le retourner en fin de parcours.
Linitialisation de ce maximum pourra se faire par le premier lment du tableau ( t[0] ), et la
boucle peut commencer par lindice 1. Le corps de la boucle consistera comparer llment courant
au maximum, et, sil lui est suprieur, le mmoriser dans le maximum.
Ce qui conduit la solution suivante :
double valeur_plus_grand (double t[], int n) {
int i;
double maxi=t[0];
for (i=1;i<n;++i) {
if (t[i]>maxi)
maxi=t[i];
}
return maxi;
}
***************************************************************************
Exercice 4. Position du maximum (*)
crivez la fonction qui retourne lindice du plus grand des n premiers lments dun tableau
de double (en cas dex-quo, lindice du premier dentre eux).
Prototype : int indice_plus_grand (double t[], int n);

Corrig
***************************************************************************

2/13
ESIEE IGI-3008 TP Langage C no 2 (corrig) 2014-2015

Le programme est semblable au prcdent. Un parcours du tableau simpose, avec une mmorisa-
tion de lindice du plus grand lment rencontr. En effet, le plus grand lment du tableau peut se
situer nimporte o (en dbut, en cours ou en fin de tableau) et donc on ne peut le connatre quaprs
avoir examin tous les lments du tableau ; mmoriser sa position permettra de la retourner en fin
de parcours.
Linitialisation de cet indice maximum pourra se faire par lindice du premier lment du tableau
( 0 ), et la boucle peut commencer par lindice 1.
Ce qui conduit la solution suivante :
int indice_plus_grand (double t[], int n) {
int i;
int imax=0;
for (i=1;i<n;++i) {
if (t[i]>t[imax])
imax=i;
}
return imax;
}

***************************************************************************
Exercice 5. Rsistance quivalente (**)
crivez une fonction qui calcule la rsistance quivalente dun nombre quelconque de rsis-
tances en parallle.
n
1 X 1
Rappel : =
R Ri
i=1
Prototype : double resistance (double r[], int n);

Corrig
***************************************************************************
Cest un schma classique daccumulation (somme des inverses des lments du tableau), puis
retour de linverse de laccumulateur. Un cueil viter est loubli du cas o un des lments est
nul, ce qui conduirait une division par 0. La solution consiste retourner directement 0 si lun des
lments est nul.
double resistance (double r[], int n) {
double req=0.0;
int i;
for (i=0;i<n;++i) {
if (r[i]==0.0)
return 0.0;
req += 1/r[i];
}
return 1/req;
}

***************************************************************************
Exercice 6. Conversion chane de caractres en entier (**)
crivez une fonction qui prend en argument une chane de caractres reprsentant un entier
en dcimal et retourne lentier quivalent. ( "123"123 ). On supposera que la chane est
correcte et reprsente bien un entier.
Prototype : int chaine_vers_entier (char *s);

Corrig
***************************************************************************

3/13
ESIEE IGI-3008 TP Langage C no 2 (corrig) 2014-2015

Une chane reprsentant un entier est compose dune suite de caractres chiffres (compris entre
'0' et '9' ), ventuellement prcde dune caractre '' ou '+'.
Lentier correspondant un caractre chiffre est gal ce caractre moins le caractre '0' . ( '2''0'
2)
Sil y a un signe, on le mmorise sous le forme dune variable valant 1 ou +1. Puis, on accumule
dans un rsultat le nombre reprsent selon le principe suivant :
pour chaque chiffre, on multiplie par 10 la valeur accumule et on ajoute la valeur du chiffre.
En fin de calcul, on retourne le rsultat 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;
}

Note : cest ainsi que les fontions atoi (conversion dune chane en int ) et atol (conversion
dune chane en long ) de la librairie libc oprent. Elles permettent galement la conversion dune
chane exprimant un nombre en base 8 (commenant par un 0) ou en base 16 (commenant par 0x
ou 0X). De plus, elles interrompent le calcul lorsquelles parviennent un caractre non autoris et
retournent le rsultat courant.
***************************************************************************
Exercice 7. Miroir (*)
crivez une fonction qui prend en argument une chane de caractres, la renverse sur elle-
mme ( "toto""otot" ) et retourne ladresse de cette chane.
Prototype : char * miroir (char *s);

Corrig
***************************************************************************
Il faut dj parcourir la chane pour accder son dernier caractre. Puis permuter les couples de
caractres symtriques en vitant de le faire deux fois, ce qui laisserait la chane inchange.
char * miroir (char * s) {
int g,d; /* indice 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 8. Rptition (*)
crivez une fonction qui prend en argument une chane de caractres et laffiche en rptant
chaque caractre n fois (lappel avec "toto" et 3 affichera "tttoootttooo" ).

4/13
ESIEE IGI-3008 TP Langage C no 2 (corrig) 2014-2015

Prototype : void repete (char *s, int n);


Corrig
***************************************************************************
Deux boucles imbriques : lune pour parcourir la chane, lautre pour rpter laffichage du ca-
ractre :
void repete (char *s, int n) {
int j;
for(;*s!='\0';++s) {
for (j=0;j<n;++j) {
printf("%c",*s);
}
}
}
***************************************************************************
Exercice 9. Mise en majuscules (*)
crivez une fonction qui prend en argument une chane de caractres, la transforme en ma-
juscules ( "toto""TOTO" ) et retourne son adresse. On laissera inchangs les caractres non
lettre. On supposera la chane sans caractres accentus ou cdills.
Prototype : char * majuscule (char *s);
Corrig
***************************************************************************
Lcart entre majuscules et minuscules en ASCII est de 32 ((20)hexa ). Ce nest pas la peine de
sen souvenir : a-A32
char * majuscule (char *s) {
char *ret = s;
for(;*s!='\0';++s) {
if ('a'<=*s && *s<='z') {
*s += 'A'-'a'
}
}
return ret;
}
***************************************************************************
Exercice 10. Recherche de motif (1) (**)
crivez une fonction qui prend en argument deux chanes de caractres et retourne 1 si la
premire chane commence par la seconde (les premiers caractres de la chane1 sont ceux de la
chane2) et 0 sinon. crivez un programme pour tester cette fonction.
Prototype : int debute_par (char * chaine1, char * chaine2);
Corrig
***************************************************************************
Il suffit de regarder pour chaque caractre de chaine2 sil est gal au caractre correspondant de
chaine1. Ds quil y a ingalit, on retourne 0 (faux). Si on arrive en bout de chaine2 sans avoir
dtect dingalit, 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;

5/13
ESIEE IGI-3008 TP Langage C no 2 (corrig) 2014-2015

return 1;
}
***************************************************************************
Exercice 11. Recherche de motif (2)(**)
crivez une fonction qui prend en argument deux chanes de caractres et retourne la position
de la premire occurrence de la chane2 dans la chane1 si elle y est prsente et -1 sinon.
Prototype : int presence (char * chaine1, char * chaine2);
Corrig
***************************************************************************
Un appel itratif debute_par permettra de dtecter la prsence de la sous-chane.
Note : ce programme considre que la chane vide dbute toute chane (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 12. Frquence (**)
crivez une fonction qui compte le nombre doccurrences dun caractre c dans une chane s.
La fonction devra tre rcursive. crivez un programme pour tester cette fonction.
Prototype : int compte (char c, char * s)
Corrig
***************************************************************************
int compte (char c, char * s) {
if (*s=='\0') {
return 0;
}
return compte(c,s+1) + (*s==c?1:0)
}
***************************************************************************
Exercice 13. Chercher/remplacer (**)
crivez une fonction qui recherche dans une chane chaque caractre c pour le remplacer par
un caractre r et retourne ladresse de la chane.
Prototype : char * cherche_remplace (char c, char r, char * s);
Corrig
***************************************************************************
Cest un parcours simple avec remplacement lorsque lon tombe sur le caractre 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);
}
***************************************************************************

6/13
ESIEE IGI-3008 TP Langage C no 2 (corrig) 2014-2015

2 Mmoire et pointeurs

Exercice 14. Reprsentation mmoire (*)


Soit le morceau de programme suivant :

int a;
int *pa;
double x;
int **p;
a=8;
pa=&a;
x=3.14159;
p=&pa;
**p=281;

En supposant que la mmoire soit structure en octets, que les entiers soient cods sur 4
octets, les pointeurs sur 4 octets et que la zone de mmoire automatique soit situe en dbut
dexcution ladresse 2004, reprsentez la mmoire finale de ce programme.
Corrig
***************************************************************************
Aprs lexcution de ce programme, la mmoire ressemblera au schma suivant :
variable adresse mmoire contenu
a 2004 281
2005
2006
2007
pa 2008 2004
2009
2010
2011
x 2012 3.14159
2013
2014
2015
2016
2017
2018
2019
p 2020 2008
2021
2022
2023
La dernire instruction (p=281;) sera value comme suit :
laffectation =281; va mettre 281 en mmoire. Mais o ?
p vaut 2008
p est la mmoire dont ladresse est la valeur de p (2008), donc p est le pointeur dentier (p
est un int , donc p est un int ) situ en 2008 et sa valeur est 2004

7/13
ESIEE IGI-3008 TP Langage C no 2 (corrig) 2014-2015

p est la mmoire dont ladresse est la valeur de p (2004), donc lentier (p est un int ,
donc p est un int ) situ en 2004.
laffectation p=281; va mettre 281 dans la mmoire situe en 2004, donc indirectement
dans a. Do la valeur finale de a : 281.
***************************************************************************
Exercice 15. changes (1) (*)
Soit la fonction suivante :

void echange1 (int x, int y) {


int z;
z=x;x=y;y=z;
}

Pourquoi ne fonctionne-t-elle pas lorsquon lappelle avec par exemple


a=2;b=3;echange1(a,b) ?
Reprsentez la mmoire lors de lexcution de ce morceau de programme.
Corrig
***************************************************************************
Les instructions a=2;b=3; mettent 2 et 3 dans deux zones mmoires de type int (supposes
alloues pralablement par une dclaration)
int a int b
2 3
Lappel echange1(a,b); alloue automatiquement deux zones mmoires de type int (les pa-
ramtres formels x et y de la fonction echange1 ) et recopie les valeurs de a et b dans ces
zones mmoires :
int a int b int x int y
2 3 2 3
La dclaration int z; alloue automatiquement une zone mmoire de type int non initialise
(contenu quelconque marqu ? ? ? ?) :
int a int b int x int y int z
2 3 2 3 ????
Linstruction z=x; recopie le contenu de x dans z :
int a int b int x int y int z
2 3 2 3 2
Linstruction x=y; recopie le contenu de y dans x :
int a int b int x int y int z
2 3 3 3 2
Linstruction y=z; recopie le contenu de z dans y :
int a int b int x int y int z
2 3 3 2 2
Les variables x et y ont bien t permutes, mais notez bien que les originaux a et b sont
rests inchangs.
Sur laccolade fermante (fin dexcution de la fonction echange1 , les mmoires alloues auto-
matiquement pour la fonction sont restitues.
Rsultat des courses : on se retrouve dans la mme situation quavant lappel :

8/13
ESIEE IGI-3008 TP Langage C no 2 (corrig) 2014-2015

int a int b
2 3
Tout ce travail pour rien...
***************************************************************************
Exercice 16. changes (2) (*)
Soit la fonction suivante :

void echange2 (int *x, int *y) {


int *z;
*z=*x;*x=*y;*y=*z;
}

Pourquoi risque-t-elle ne pas fonctionner lorsquon lappelle avec par exemple


a=2;b=3;echange2(&a,&b) ?
Reprsentez la mmoire lors de lexcution de ce morceau de programme.
Corrig
***************************************************************************
Les instructions a=2;b=3; mettent 2 et 3 dans deux zones mmoires de type int (supposes
alloues pralablement par une dclaration et situes par exemple aux adresses 2008 et 2012)
2008 2012
int a int b
2 3
Lappel echange2(&a,&b); alloue automatiquement deux zones mmoires de type int * (les
paramtres formels x et y de la fonction echange2 ) et recopie les valeurs de &a (2008) et
&b (2012) dans ces zones mmoires :
2008 2012
int a int b int *x int *y
2 3 2008 2012
La dclaration int *z; alloue automatiquement une zone mmoire de type int * non initialise
(contenu quelconque marqu ? ? ? ?) :
2008 2012
int a int b int *x int *y int *z
2 3 2008 2012 ????
Linstruction *z=*x; recopie la valeur de *x (cest--dire la valeur de la zone mmoire dont
ladresse est donne par x , soit la zone mmoire int situe en 2008, donc 2) dans la zone mmoire
dont ladresse est donne par z . Or, z ntant pas initialise, la zone mmoire quelle indique peut
tre situe nimorte o.
Si lon a de la chance, elle sera interdite en criture et un message de type
Segmentation fault
nous avertira de lerreur de programmation, avant larrt du programme.
Si lon est moins chanceux, cette zone mmoire ne sera pas interdite en criture et le programme,
quoique faux, pourra sembler fonctionner comme dans echange3 (voir ci-dessous).
Dans tous les cas, ce programme est faux et il ne faut jamais utiliser une variable sans lavoir
initialise.
***************************************************************************
Exercice 17. changes (3) (*)

9/13
ESIEE IGI-3008 TP Langage C no 2 (corrig) 2014-2015

Soit la fonction suivante :


void echange3 (int *x, int *y) {
int z;
z=*x;*x=*y;*y=z;
}
et lappel suivant :
a=2;b=3;echange3(&a,&b)
Reprsentez la mmoire lors de lexcution de ce morceau de programme.
Corrig
***************************************************************************
Les instructions a=2;b=3; mettent 2 et 3 dans deux zones mmoires de type int (supposes
alloues pralablement par une dclaration et situes par exemple aux adresses 2008 et 2012)
2008 2012
int a int b
2 3
Lappel echange3(&a,&b); alloue automatiquement deux zones mmoires de type int * (les
paramtres formels x et y de la fonction echange3 ) et recopie les valeurs de &a (2008) et
&b (2012) dans ces zones mmoires :
2008 2012
int a int b int *x int *y
2 3 2008 2012
La dclaration int z; alloue automatiquement une zone mmoire de type int non initialise
(contenu quelconque marqu ????) :
2008 2012
int a int b int *x int *y int z
2 3 2008 2012 ????
Linstruction z=*x; recopie la valeur de *x (cest--dire la valeur de la zone mmoire dont
ladresse est donne par x , soit la zone mmoire int situe en 2008, donc 2) dans la zone mmoire
z de type int :
2008 2012
int a int b int x int y int z
2 3 2008 2012 2
Linstruction *x=*y; recopie le contenu de *y (soit le contenu de la zone mmoire de type
int dont ladresse est donne par la valeur de y , soit 3), dans *x (soit la zone mmoire de
type int dont ladresse est donne par la valeur de x , soit la zone mmoire situe en 2008, cest
dire a ) :
2008 2012
int a int b int *x int *y int z
3 3 2008 2012 2
Linstruction *y=z; recopie le contenu de z (2) dans *y (soit la zone mmoire de type
int dont ladresse est donne par la valeur de y , soit la zone mmoire situe en 2012, cest dire
b ):
2008 2012
int a int b int *x int *y int z
3 2 2008 2012 2

10/13
ESIEE IGI-3008 TP Langage C no 2 (corrig) 2014-2015

Les variables a et b ont bien t permutes.


Sur laccolade fermante (fin dexcution de la fonction echange3 , les mmoires alloues auto-
matiquement pour la fonction sont restitues.
Rsultat des courses : la fonction echange3 a bien fonctionn.
2008 2012
int a int b
3 2
Ouf !
***************************************************************************
Exercice 18. changes (4) (**)
Soit la fonction suivante
void echange(type *x, type *y) {
*y=*x + *y;
*x=*y - *x;
*y=*y - *x;
}
ou type peut tre double ou int . Faites tourner cette fonction la main avec par exemple :
int a,b; a=2;b=3;echange(&a,&b)
Fonctionne-t-elle dans tous les cas ?
Donnez un exemple avec des double et un exemple avec des int o elle est incorrecte.
Corrig
***************************************************************************
Si par exemple, a vaut 1e10 et b vaut 1e-10 , la diffrence entre a et b sera value
0, la prcision des double tant denviron 15 chiffres significatifs. Donc lchange, au lieu de
permuter les valeurs, considrera la plus petite comme nulle.
***************************************************************************
Exercice 19. Pointeurs vs. tableau (**)
Soit les deux dclarations suivantes :
char tabString[]="toto";
char *ptrString="Titi";
Essayez den trouver les diffrences de comportement. En particulier, notez les possibilits
dexcuter les instructions suivantes :
tabString[2]='e';
ptrString[2]='e';
tabString=ptrString;
ptrString = tabString;
Lesquelles provoquent une erreur la compilation ? lexcution ? Faites un dessin reprsen-
tant la mmoire.
Corrig
***************************************************************************
tabString[2]='e'; // possible
ptrString[2]='e'; // impossible
tabString=ptrString; // impossible
ptrString = tabString; // possible

11/13
ESIEE IGI-3008 TP Langage C no 2 (corrig) 2014-2015

***************************************************************************
Exercice 20. Copie (**)
crivez la fonction qui copie les n premiers lments dun tableau source de double dans
le tableau destination de double et retourne ladresse du tableau destination. On prendra en
compte le fait que les deux tableaux peuvent se recouvrir partiellement.
Prototype : double * copie (double * destination, double * source, int n);

Corrig
***************************************************************************
La solution naturelle est base sur le principe : on parcourt paralllement les deux tableaux et on
copie lment par lment le contenu du tableau source dans le tableau destination, ce qui donne :
double *copie(double *destination, double *source, int n) {
/* Attention : ne fonctionne que si
* les tableaux ne se recouvrent pas
*/
int i;
for (i=0 ; i<n ; ++i) {
destination[i] = source[i];
}
return destination;
}
Une erreur peut survenir lorsque lon appelle cette fonction avec deux tableaux se recouvrant. Par
exemple, avec le morceau de programme suivant :
double tab[]={8,2,103,-4,5,8};
copie(&tab[1],tab,5);/* tentative de decalage droite */
on voudrait dcaler dune position vers la fin de tableau les cinq premiers lments. Or la fonction
va craser succesivement les lments du tableau quelle na pas encore copis, ce qui conduira au
rsultat suivant :
8 8 8 8 8 8
alors que lon aurait voulu obtenir :
8 8 2 103 -4 5
Faire la boucle dans lautre sens (de la fin vers le dbut) poserait le mme problme pour un
dcalage vers la gauche.
Une solution consiste tester la position de la source par rapport la destination :
si source est avant destination, on copie de la fin vers le dbut
si source est aprs destination, on copie du dbut vers la fin
Pour tester le positionnement des deux tableaux, il suffit de les soustraire. La diffrence de deux
pointeurs de mme type donne le nombre dlments de ce type situs entre ces deux pointeurs
( &t[5] - &t[3] vaut 2, &t[3] - &t[5] vaut 2, quel que soit le type de t .
Ce qui conduit :
double *copie2(double *destination, double *source, int n) {
/* fonctionne meme si les tableaux se recouvrent */
int i;
if (destination - source < 0 ) {
for (i=0 ; i<n ; ++i) {
destination[i] = source[i];
}
}
else {
for (i=n-1 ; i>=0 ; --i) {
destination[i] = source[i];

12/13
ESIEE IGI-3008 TP Langage C no 2 (corrig) 2014-2015

}
}
return destination;
}
Attention : les fontions memcpy (copie de zones mmoires) et strcpy (copies de chanes
de caractres) de la librairie libc ne grent pas les recouvrements. Ci-dessous, un extrait de la
documentation de memcpy :

If the regions overlap, the behavior is undefined.

***************************************************************************

13/13