Beruflich Dokumente
Kultur Dokumente
Eléments de contenu :
•Les opérateurs arithmétiques
•La conversion implicite de type dans les expressions.
•Les opérateurs relationnels
•Les opérateurs logiques
•L’opérateur d’affectation simple
•Les opérateurs d’incrémentations et de décrémentation
•Les opérateurs de manipulations de bits
•Les opérateurs d’affectation élargie
•L opérateur conditionnel
•Les con versions explicites
•L’opérateur de “cast”
•L opérateur séquentiel
•L opérateur sizeof
1- LES OPERATEURS ARITHMETIQUES
I.1 Leur rôle
On trouve en C
• 5 opérateurs BINAIRES “ (c’est à dire qui portent sur deux opérandes)
• 2 opérateurs unaires qui portent sur un seul opérande.
Le tableau suivant vous fournit la liste de ces différents opérateurs
TYPE OPERATEUR ROLE
Binaire + somme
- différence
* produits
/ quotient
% reste de division
(modulo)
Unaire - opposé
+ Identité
A l’exception de l’opérateur “%“ qui ne peut porter que sur des entiers, ces opérateurs peuvent
s’appliquer à tous les types numériques. Cela signifie qu’un même symbole tel que ”*” définit
on fait plusieurs opérations différentes suivant le type des quantités sur lesquelles il porte.
Les opérateursBINAIRES ne sont, à priori, définie que pour des opérandes de même type et
ils fournissent un résultat de ce même type. Ce dernier point mérite un peu d’attention. En effet,
si ‘on s’attend effectivement à ce que la somme e deux entiers soit un entier, les choses sont
moins évidentes pour l’opérateur quotient (“/”). Ainsi par exemple, 5/2 est le quotient de deux
valeurs doubles, et à ce titre fournit bien le résultat 2.5 qui est aussi (le type double. Par contre
5/2 est le quotient de deux valeurs de type int et le résultat est de type int, a savoir ici 2.
Remarque
Il n’existe pas d’opérateur d’élévation à la puissance, il est nécessaire de faire appel soit â des
produits successifs, soit à la fonction power de la bibliothèque.
L2 Leurs priorités relatives
Lorsque plusieurs opérateurs apparaissent. dans une même expression, il est nécessaire de
savoir dans quel ordre ils sont mis enjeu. En C, comme dans les autres langages, les règles sont
naturelles et rejoignent celles de l’algèbre traditionnelle ( en ce qui concerne les opérateurs
arithmétiques)
Les opérateur unaire + et - ont la priorité la plus élevée, On trouve on suite à un même niveau
es opérateurs *,/ et % . Enfin sur un dernier niveau apparaissent les opérateurs binaires + et - .
En cas de priorité identique, les calculs s’effectuent de gauche à droite. Enfin, des parenthèses
permettent d’outre passer ces règles de porte en forçant e calcul préalable de l’expression
qu’elles contiennent. Notez que ces parenthèses peuvent également être employées jour assurer
une meilleure lisibilité d’une expression.
Voici quelques exemples d’expressions équivalentes
a+b*c a+(b* c)
a*b+c%d (a*b)+(c%c)
-c % d (-c)%d
-a/-b+c ((-a)/(-b))+c
Il- LES CONVERSIONS IMPLICITES DE TYPE DANS LES EXPRESSIONS
A l’opposé de ce qui se passe dans un langage comme Pascal, Le langage C est très tolérant vis
a vis du mélange des types dans les expressions.
Ainsi, par exemple, si n est le type int, et x de type double, une expression telle que:
n+x
sera acceptée par le compilateur. Elle conduira à la mise en place d’instructions réalisant :
• la conversion de n en double. Cette opération a un sens et est toujours possible.
• l’addition des deux valeurs de type double. Le résultat sera lui aussi de type double.
On dit qu’il y a eu mise en place de “conversions implicites” puisque celles—ci n’ont pas été
explicitées au sein des instructions.
D’une manière générale, on peu dire que si un opérateur porte sur deux opérandes de type
différents, il y a en quelque sorte, conversion du type “le plus faible’ dans le type “le plus fort”.
Ainsi, dans notre exemple double était plus fort que int.
Une grande originalité du C est d’étendre ces facilités de conversions aux types caractères.
Cette fois cependant le mécanisme sera un peu diffèrent car tout opérande de
type char apparaissant dans une expression sera systématiquement converti en int avant
d’intervenir dans les calculs.
Il est à noter aussi que tous les calculs flottants ne s’effectuent que sur des valeurs de
type double. Pour ce faire tout opérande de type float est systématiquement converti en double.
11.1 Exemples de conversions implicites sur des types numériques
Supposons que nous avons effectué les déclarations suivantes :
int, p
Long q, r
float x
double z
Voici trois exemples d’expressions et la manière dont elles vont être évaluées :
n+q
La valeur de n est convertie en long avant d’être ajoutée à celle de q. Le résultat est de
type long
p + n *p
Le produit n * p est évalué en int puis le résultat est converti en long pour être ajouté à. la
valeur de q. Le résultat est de type long
x*n
Les valeurs de x et de n sont converties en double . Le résultat est de type double.Bien
entendu, rien n’empêchera ce résultat d’être assigné à une variable de type floa.t(moyennant
une conversion supplémentaire ).
II .2 Exemples de conversions implicites de char et int
Supposons que nous avons effectué les déclarations suivantes :
int n
Char cl , c2
Voici, à nouveau quelques exemples d’expressions et la manière dont elles sont évaluées :
C1 + 1
La valeur de cl est d’abord convertie on int. Cette conversion revient en fait à considérer que
les 8 bits occupés par cl forment une valeur entière codée de la même manière que les entiers
(mais sur 8 bits aura lieu de 16). Il faudra toutefois distinguer deux cas suivant que cl possède
l’attribut signed ou unsigned :
• si cl est unsigned la conversion revient effectivement à prendre la valeur du code ASCII du
caractère on obtiendra ainsi, une valeur entière comprise entre 0 et 255.
• si cl est signed, le premier des 8bits Sera interprété comme un bit de signe. On obtiendra
ainsi une valeur entière comprise entre -128 et 127.
au résultat de cette conversion, on ajoutera la valeur entière de 1.
cl + n
Là encore, la valeur de cl sera convertie en int. Le résultat sera ajoutée la valeur n pour fournir
une valeur de type int.
c2 - cl
Ici, bien que les deux opérandes soient de type char, il y aura néanmoins conversion préalable
des valeurs de cl et c2 on int .avant qu’en soit calculée la différence. Le résultat est de type int.
II-3 Règles générales de con version implicite
a) Conversions systématiques et conversion d’ajustement de type
Comme on a vu dans es exemples précédents, il faut distinguer les conversions qui ont lieu
systématiquement de celles qui ne sont réalisées qu’en cas de besoin par ”absorption” par un
type plus fort.
Les conversions systématique se résument
char —> int
float —> double
Quant aux autres conversions, elles permettent de faire porter un opérateur sur des valeurs
de même type. Le type commun étant imposé par celui des opérandes ayant le type le plus fort.
Voici le schéma récapitulant ces règles. Les flèches horizontales correspondent à des
conversions systématiques. Les flèches verticales correspondent â des conversions d’ajustement
de type.
char —> int —> long —> float —> double
De plus, ces règles doivent être complétées par la manière dont sont pris en compte les attributs
signed et unsigned. Pour ce faire on va distinguer le type char des types entiers.
b) Prise en compte de l’attribut signe pour les caractères
Cet attribut intervient lors de la conversion systématique en int. S’il s’agit
d’un caractère non signé le résultat de la conversion est un entier compris entre O et 255 ; la
conversion revient simplement dans ce cas à compléter le motif de 8 bits par des zéros à
gauche. Lorsqu’il s’agit d’un caractère signé, le résultat de la conversion est un entier compris
entre - 128 et +127 ; la conversion revient à “propager le bit de signe du caractère dans les 8
bits de gauche de L’entier à fabriquer, <i C~) ~r Ct)
c) prise en compte de l’attribut signe pour les entiers
En ce qui concerne la prise en compte de l’attribut signe pour les types entiers, la règle prévoir
que unsigned .
III- LES OPERATEURS RELATIONNELS
Comme tout Langage, C permet de ‘‘comparer” les expressions à laide d’opérateurs classiques
de comparaison. En voici un exemple :
2 * a> b+5
Par contre, C se distingue des autres langages sur deux points :
.Le résultat de la comparaison est, non pas une valeur booléenne (on dit aussi ‘logique”)
prenant l’une des deux valeurs vrai ou faux, mais un entier valant :
0 si le résultat de a comparaison est faux
1 si le résultat de la comparaison est vrai.
Ainsi la comparaison ci-dessus devient en fait une expression de type entier. Cela signifie
quelle pourra éventuellement intervenir dans des calculs arithmétiques
.les expression comparées pourront être d’un type de base quelconque et elles seront soumises
aux règles de conversion présentées dans le paragraphe précédent. Cela Signifie qu’au bout du
compte on ne sera amené à comparer que des expression des types numérique.
Le tableau suivant fournit la liste des opérateurs relationnels existant en C. Remarquez bien que
la notation (==)de l’opérateur de légalité, le signe (=)étant , comme nous le verrons, réservé aux
affectations.
OPERATEUR SIGNIFICATION
< Inférieur à
<= Inférieur ou égal à
> Supérieur à
>= Supérieur ou égal à
== Egal à
!= Différent de
En ce qui concerne leurs priorités, il faut savoir que les quatre premiers opérateurs (<, <=,
>, <=) sont de même priorité. Les deux derniers (== et ! =) possédant également la même priori
té, mais celle-ci est inférieure à celle des précédents. Ainsi l’expression
a <b == c< d
est interprétée comme suit
(a <b)== (c< d)
ce qui, en C, a effectivement une signification, compte tenu de ce que les expressions a<b et
c<d sont, finalement, des quantités entières.
D’autre part ,ces opérateurs relationnels sont moins prioritaires, les opérateurs arithmétiques.
Cela permet souvent d’éviter certaines parentheses dans des expressions. Ainsi :
x + y < a+2
est équivalent à
(x+y ) < (a + 2)
IV- LES OPERATEURS LOGIQUES
C dispose de trois opérateurs logiques classiques
OPERATEUR SIGNIFICATION
&& et
II ou(inclusif)
! négation
Par exemple :
(a < b) && (c < d )
prend la valeur 1 (vrai) si les deux expressions a <b et c < d sont toutes deux vrais(de
Pour éviter de calculer soi-même la taille d’objets d’un type relativement complexe pour lequel
on ne sera pas certain de la manière dont il sera “implémenté” par le compilateur. Ce sera
notamment des structures.
Le tableau suivant fournit la liste complète de tous les opérateurs du C, classés par ordre de
priorité décroissante, accompagnés de leurs mode d’associativité.
Notez bien qu’en langage C, un certain nombre de notations servant à “référencer” des objets
sont considérés comme des opérateurs et, entant que tels, soumises à des régies de priorités Ce
sont essentiellement
les références à des élément d’un tableau réalisées par [ ],
des références à des champs de structures : opérateurs - > et . ,
des opérateurs d’adressage : * et &.
Ces opérateurs seront étudier ultérieurement dans les chapitres correspondant, néanmoins, ils
figurent dans le tableau proposé
CATEGORIE OPERATEURS ASSOCIATIVITE
Référence () [] -> . —>
Unaire + - ++ -- ! ~ * & (cast) sizeof <—
Arithmétique */ % —>
Arithmétique +- —>
Décalage << >> —>
Relationnel < <=> > = —>
Relationnel == != —>
Manipulation de bits & —>
Manipulation de bits ^ —>
Manipulation de bits | —>
Logique && —>
Logique || —>
Conditionnel :? —>
Affectation = += -= *= /= %= &= ^= |= <<= <—
>>=
Séquentiel , —>
Objectifs
• Connaître les différentes instructions de contrôle du langage C.
• Connaître la syntaxe de chacune de ces instructions.
Eléments de Contenu
• L’instruction if
• L’instruction switch
• L’ Instruction do … while
• L’instruction while
• L’instruction for
• Les instructions de branchement inconditionnel : break, continue et goto.
A priori, dans un programme, les instructions sont exécutées séquentiellement, c’est à dire dans
L’ordre où elles apparaissent. Or, la puissance et le “comportement intelligent” d’un
programme
Proviennent essentiellement :
• de la possibilité d’effectuer des “choix’, de se comporter différemment suivant les
“circonstances” (celles-ci pouvant être, par exemple, une réponse de l’utilisateur, un résultat de
calcul, …).
X
by Counterflix
• de la possibilité d’effectuer des “boucles”, c’est à dire de répéter plusieurs fois un ensemble
donné d’instructions.
Tous les langages disposent d’instructions, nommées “ instructions de contrôle’’, permettant de
réaliser Ces choix ou ces boucles. Suivant le cas, celles-ci peuvent être :
• basées essentiellement sur la notion de branchement (conditionnel ou inconditionnel); c’est,
par exemple, le cas du langage Basic,
• ou, au contraire, traduire fidèlement les structures fondamentale de la programmation
structurée; c’est, par exemple, le cas du langage Pascal bien qu’eu toute rigueur, ce dernier
dispose d’une instruction de branchement inconditionnel GOTO.
Le langage C est assez proche du pascal sur ce point puisqu’il dispose “d’instructions
structurées” permettent de réaliser :
• des choix : instructions if... elseet switch
• des boucles : instructions : do…while, while et for
Toutefois, la notion du branchement n’est pas totalement absente du langage C puisque, il
dispose (l’instructions de branchement inconditionnel :goto, break et continue,
Ce sont ces différentes instructions de contrôle du langage C que nous nous proposons d’étudier
dans ce chapitre.
I-L’INSTRUCTION IF
I.1 Exemples d’introduction de l’instruction if
Soit l’exemple suivant :
if(a<=b)
printf(”croissant”);
e/se
printf(”décroissant’);
Ces quelques lignes constituent une instruction if Elles s’interprètent ainsi :si la condition a <=
b est vraie (c’est à dire non nulle), on exécute l’instruction :
printf(”croissant”);
dans le cas contraire, on exécute l’instruction suivant le mot eIse :
printf(’décroissant ”);
Dans ce premier exemple, chacune des deux parties du choix se limite à une seule instruction
simple. Il est possible d’en placer plusieurs en les regroupant en ce que l’on nomme un “bloc”.
En voici un exemple :
if(a<=b)
{
Printf(”croissant ’);
max.=b ;
}
e/se
{
printf(”décroissant”);
max =a;
}
Le bloc joue un rôle important un en C et il peu être employé ailleurs que dans une instruction
if .Etudions cette notion on détail.
1.2 Les blocs d’instructions
Il s’agit d’une suite d’instructions placées entre { et } . Les instructions figurant dans un bloc
sont absolument quelconques. IL peut s’agir aussi bien d’instructions simples (terminés par un
point—virgule) que d’instructions structurées (choix, boucles) lesquelles peuvent à leur tour
renfermer d’autre blocs...
Vous voyez qu’il y a on C une sorte de récursivité de la notion d’instruction. D’une manière
générale, dans la description de la syntaxe des différentes instructions, nous seront souvent
amenés à mentionner ce terme d’instruction, celui désignera toujours n’importe quelle
instruction C simple, structuré ou un bloc.
Un bloc peut se réduire à une seule instruction, voire même être vide. Voici deux exemples de
bloc corrects :
{}
{ i=1 ; }
Le second bloc ne présente aucun intérêt en pratique puisqu’il pourra toujours être remplacé par
‘instruction simple qu’il contient.
1.3 Syntaxe de l‘instruction if
Le mot else et l’instruction qu’il introduit sont facultatifs, de sorte que cette instruction if
présente deux formes.
if (expression) if (expression)
instruction 1 instruction 1
else
instruction 2
avec,:
• expression :expression quelconque.
• instruction I et instruction2 instructions quelconques, c’est à dire
- simple,
- bloc,
- instruction structurée.
Remarque :
La syntaxe de cette instruction n’impose en soi aucun point-virgule, s’ ce n’est ceux qui
terminent naturellement les instructions simples qui y figurent.
1.4Quelques exemples
l’expression conditionnant le choix est quelconque. La richesse de la notion d’expression en C
fait que celle-ci peut elle-même réaliser certaines actions. Ainsi :
if(++i <limite) printf(”ok’’);
est équivalent à
i=i+1
if (i< limite) printf(”ok’’);
de même:
if((c= getchar())!=’\n’)…
peut remplacer
c= getchar() ;
if(c !=’\n’)…
Par contre, if faut noter que:
if((+ +i< limite)&& ((c =getchar())!=’\n’))…
n’est pas équivalent à :
++i ;
c = getchar()
if((I<limite) && (c!= ‘\n’))
car, comme nous l’avons déjà dit, l’opérateur && n’évalue le second opérande que lorsque cela
est nécessaire. Autrement dit, dans la première formulation, l’expression :
c= getchar()
n’est pas évaluée lorsque la condition + +i<limite est fausse ; elle l’est par contre dans a
deuxième formulation.
1.5 Le cas des instructions if imbriquées
Nous avons déjà mentionné que les instructions figurant dans chaque partie du choix d’une
instruction if pouvaient être absolument quelconques. En particulier, elles peuvent à leur tour
renfermer des instructions if .Or, compte tenu de ce que cette instruction peut comporter ou ne
pas comporter de ‘’else” il existe certaines situations où une ambiguïté apparaît. C’est le cas
dans cet exemple :
if(‘a<=b) if(b<=c) printf (‘‘ordonné”);
else printf (‘‘non ordonné ‘‘) ;
Est-il interprété comme le suggère cette présentation
if(a<=b) if(b<=c) printf (‘‘ordonné”);
else printf(“non ordonné”);
Ou bien comme le suggère celle-ci :
if(a<=b) if(b<=c) printf (‘‘ordonné”);
else printf(“non ordonné”);
La première interprétation conduirait â afficher « non ordonné lorsque la condition a<=b est
fausse, tandis que la seconde n’afficherait rien dans ce cas la. La règle adoptée par le langage C
pour lever une telle ambiguïté est la suivante :
Un else se rapporte toujours au dernier if rencontré auquel un else n’est pas encore
attribué.
Ainsi, dans notre cas c’est la deuxième représentation qui suggère le mieux ce qui se passe.
II - L’INSTRUCTION SWITCH
11.1 Exemples d’introduction à l’instruction switch
a) premier exemple
Voyons ce premier exemple de programme accompagné de trois exemples d’exécution.
main ()
{
int n ; Donner un entier : 0
printf ( “donnez un entier entier : “); nul
scanf(”%d”, &n); au revoir
switch( n)
{ Donner un entier :1
case O : printf (“nul\n“); un
break; au revoir
case 1: printf (“un\n“);
break;
case 2 : printf (“deux\n”); Donner un entier : 2
break; deux
} au revoir
printf(“au revoir\n”);
}
L’instruction switch s’étend ici sur 9 lignes (elle commence au mot switch). Son exécution se
déroule comme suit : On commence tout d’abord par évaluer expression figurant après le mot
switch (ici n). En suite en recherche dans le bloc qui suit s’il existe une étiquette de ta
forme “case x” correspondant à la valeur ainsi obtenue. Si c’est e cas, on “se branche” à
l’instruction figurant après cette étiquette. Dans le cas contraire on passe à instruction qui suit le
bloc.
ainsi, par exemple quand n vaut 0, on trouve effectivement une étiquette “case 0” et l’on
exécute Instruction correspondante, c’est à dire :
pintf (“nul\n“) ;\
On passe ensuite, naturellement, à l’instruction suivante, à savoir ici :
break;
Celle-ci demande en fait de sortir du bloc. Le rôle de cette instruction est fondamental. Voyez, à
titre d’exemple, ce que produirait ce même programme en l’absence de break.
main()
{
int n ; Donner un entier : 0
printf(“donnez un entier : “) ; nul
scanf(“%d,&n) ; un
switch(n) deux
{ au revoir
case 0 :printf(“nul\n“); Donner un entier :2
case1:printf(“un\n“) ; deux
case2:printf(“deux\n“) ; au revoir
}
printf(“au revoir\n“) ;
}
b) l’étiquette“ default“
Il est possible d’utiliser le mot-clé “default” comme étiquette à laquelle je programme
se “branchera“ dans le cas où aucune valeur satisfaisante n’aura été rencontrée. En voici un
Exemple :
main()
{
int n ; donnez un entier :
printf(“donnez un entier : “) ; deux
scanf(“%d“ ;&n) ; au revoir
switch (n)
{
case 0 :printf(“nul\n“); donner un entier:25
break; grand
case 1 :printf(“un\n“); au revoir
break ;
case 2 :printf(“deux\n“);
break ;
default :printf(“grand \n“);
}
printf(“au revoir \n“);
}
II.2 Syntaxe de l’ instruction switch
Switch(expression)
{
case constante 1 :[ suite_d’instruction_1]
case constante 2 : [ suite d’instruction_ 2]
case constante_ n :[ suite d’instruction_n]
default :[ suite_d’instruction]
}
Avec:
Constante : expression constante entière (mais char est accepté car il sera converti en int). Suite
_d’instruction : séquence d’instructions quelconques.
N.B. : les crochets ([et]) signifient que ce qu’ils renferment est facultatif.
IIl- L’INSTRUCTION DO …WHILE
III.1 Exemple d’introduction de l’instruction do ... while
main()
{
int n ;
do donner un nombre positif: -5
{ vous avez fourni -5
printf (“donner un nombre positif : ‘‘) ; donner un nombre positif: - I
scanf (‘‘%d’’, &n ) ; vous avez fourni –1
donner un nombre positif: 3
vous avez fourni 3
}
réponse correcte
while (n<=0) ;
printf (“réponse correcte‘‘) ;
}
l’instruction
do{…} while (n<=0)
répète l’instruction qu’elle ”contient” (ici un bloc) tant que la condition mentionnée (n<=0) est
vraie (c’est à dire en C non nulle). Autrement dit, dans l’exemple ci-dessus, elle demande un
nombre à l ‘utilisateur tant qu’il ne fournit pas une valeur positive.
On ne sait pas à priori combien de fois cette boucle sera répétée. Toutefois elle est toujours
parcourue au moins une fois. En effet la condition qui régit cette boucle n’est examinée qu’à.
La fin de chaque répétition (puisque la partie while figure en fin).
Il faut noter que la sortie de la boucle ne se fait qu’après un parcours complet de ses
instructions et non dès que la condition mentionnée devient fausse. Ainsi, dans l’exemple ci-
dessus, même après que .l‘utilisateur ait fourni une réponse convenable, il y a exécution de
L‘instruction d’affichage:
printf (”vous avez fourni %d \n”,n) ;
111.2 Syntaxe de l’instruction do …while
do instruction
while (expression,);
Commentaires :
• Notez bien la présence de parenthèses autour de l’expression qui régit la poursuite de la
boucle et d’un point-virgule àla fin de cette instruction.
• Lorsque l’ instruction à répéter se limite à une seule instruction simple, n’omettez pas le
point- virgule qui la termine. Ainsi:
do c = getchar() while (c != ‘x’);
est incorrecte. Il faut écrire :
do c= getchar() ;while(c!=‘x’);
• N’oubliez pas que l’expression suivant le mot while peut être aussi élaborée que vous le
souhaitez et qu’elle permet ainsi de réaliser certaines actions.
• L’instruction à répéter peut être vide. toutefois, elle doit être terminée par un point-virgule.
Ainsi ces expressions sont correctes :
do; while(... )
do {} while (... )
• L’instruction :
do{} while (1)
représente une boucle infinie ; elle est syntaxique ment correcte, bien quelle ne présente en
pratique aucun intérêt.
IV- L’INSTRUCTION WHILE
IV. 1 Exemple d’introduction de l’instruction while
main()
{
int n,som ;
som=0
while(som<100)
{
printf (”donne un nombre :“); donner un nombre : 30
scanf (“%d”, &n); donner un nombre : 50
som + = n; donner un nombre :35
} somme obtenue : 115
printf(”somme obtenue : %d”,som,) ;
}
L’instruction
while (som<100)
répète l’instruction. qui suit (ici un bloc) tant que la condition mentionnée est. vraie (non nulle),
comme le ferait do... while Par contre, cette fois la condition de poursuite est examinée avant
chaque parcours de la boucle et non après. Ainsi une telle boucle peut très bien n’être
parcourue aucune fois si la condition est fausse dés le départ.
IV.2 Syntaxe de l’instruction while
While (expression)
Instruction
Commentaires :
• Notez bien la présence de parenthèses autour de l’expression qui régit la poursuite dela
boucle.
• L’expression utilisée comme condition de poursuite est évaluée avant le premier tour. Il est
donc nécessaire que sa valeur soit définie à. ce moment.
• La construction
while(expression1 , expression2) ;
est équivalente à :
do expression 1
while (expression 2)
V- L’INSTRUCTION FOR
V.1 Exemple d’introduction de I’instruction for
main; ()
{
int i; bonjour I fois
for (i=1 ; i <= 5; I++) bonjour 2fois
{ bonjour 3fois
printf (“bonjour”); bonjour 4fois
printf (‘’%dfois\n’’ ,i) ; bonjour 5fois
}
{
La ligne
for (i=I;i <=5; i++)
Comporte trois expressions. La première est évaluée (une seule fois) avant d’entrer dans la
boucle. La seconde conditionne la poursuite de la boucle elle est évaluée avant chaque parcours
.la troisième, enfin, est évaluée à la fin de chaque parcours.
Le programme précédent est en fait équivalent au suivant :
Main()
{
int I;
while(I<=5)
{
printf(“bonjour”);
printf(“%d fois\n”,i) ;
i++;
}
}
V.2 Syntaxe de l’instruction for
For([expression 1];[expression 2];[expression 3])
instruction
Commentaires :
· D’une manière générale, nous pouvons dire que :
for ([expression1] ;[ expression2 ]; [expression3]) instruction
est équivalent à :
expression1 ;
while (expression2)
{
instruction
expression3
}
Chacune des trois expressions est facultative (pas obligatoire). Ainsi, ces instruction sont
équivalentes à l’instruction for de notre premier exemple de programme :
i=1 ;
for (; i <= 5, i++)
{
printf ( “bonjour’’) ;
printf(‘’%d fois\n’’,i);
}
i=I;
for (; i <= 5;)
{
printf (“bonjour’’);
printf(‘’%d fois\n’’,i);
i++;
}
Notez que lorsque expression 2 est absente elle est considérée comme vraie.
· La richesse de a notion d’expression on C permet de regrouper plusieurs actions dans une
expression. Ainsi :
for (i=0,j=1,k=5;...;…)
est équivalent à :
j=1;
k = 5;
for (i =0;…;…)
· Les constructions :
for ( ; ; );
for ( ; ; ) {}
Sont syntaxiquement correctes. Elles représentent des boucles infinies de corps vide.
VI- LES INSTRUCTION DE BRANCHEMENT INCONDITIONNEL: BREAK,
CONTINUE ET GOTO
Ces trois instructions fournissent des possibilités diverses de branchement inconditionnel. Les
deux premières s’emploient principalement au sein de boucles tandis que la dernière est d’un
usage libre mais peu répandu, à partir du moment où on cherche à structurer ses programmes.
VI.1 L’instruction break
Nous avons déjà vu le rôle de break au sein du bloc l’une instruction switch.
Le langage C autorise également l’emploi de cette instruction dans une boucles. Dans ce cas,
elle sert à interrompre le déroulement de la boucle, en passant à l‘instruction qui suit cette
boucle. Bien entendu, cette instruction n’a d’intérêt que si son exécution est conditionnée par
un choix.
Voici un exemple montrant le fonctionnement de break.
main()
{
int i début de tout /
for (i=1 ;i<=10 ;i++) bonjour
{ fin de tour 1
printf (‘’début de tour %d\n “i) ; début de tour 2
printf (“bonjour\n”); bonjour
if(i == 3) break ; fin de tour 2
printf(‘’fin de tour %d\n’’, i); début de tour 3
} bonjour
printf (“après la boucle\n’’); après la boucle
}
Remarque
En cas de boucles “imbriquées ’’, break fait sortir de la boucle la plus interne. De même,
si break apparaît dans un switch imbriqué dans une boucle, elle ne fait sortir que du switch.
VI.2 L’instruction continue
L’instruction continue permet de passer “prématurément” au tour de la boucle suivant En voici
un premier exemple avec for :
Main()
{
int i ; début de tour 1
for (i=1 ;i <= 5; i++) début de tour 2
{ début de tour 3
printf (“début de tour %d\n “, i) ; début de tour 4
if(i<4) continue ; bonjour
début de tour 5
bonjour
}
}
et voici un second exemple avec do …while :
main ()
{
int n ; donnez un nombre > 0 :4
do son carré est: 16
{
printf (”donner un nombre >0”)
scanf (”%d”,n&n) ; donner un nombre supérieur à 0 :-5
if(n<0) svp>0 {
donner un nombre >0 :2
son carre est : 4
donner un nombre>0 :0
printf (” svp >0\n”), son carré est : O
continue
}
printf (“son carré est : %d\n “, n*n) ;
}
while(n) ;
}
Remarques :
Notez bien que lorsqu’elle est utilisée dans une boucle for, cette instruction continue effectue
bien un branchement sur l’évaluation de l’expression de fin de parcours de boucle (nommée
expression2 dans la présentation de Sa syntaxe) et non après.
En cas de boucles imbriquées, l’instruction continue ne concerne que la boucle la plus interne.
VI.3 L’instruction goto
Elle permet le branchement en un point quelconque. Et, voici Un exemple
Main()
{
int i ; début de tour 1
for (i=1 ;<=10 ;i++) bonjour
{ fin de tour 1
printf (“début de tour %d\n “,i) ; début de tour 2
printf(“bonjour\n“) ; bonjour
if(i= =3)goto sortie ; fin de tour 2
printf(“fin de tour %d\n“,i) début“ de tour 3
sortie printf (‘après la boucle\n“) ; après la boucle
}
LES FONCTIONS
Objectifs
• Comprendre la notion de fonction en C
• Connaître les arguments d’une fonction
• Connaître la notion de prototype en C
• Différencier entre les variables globales et les variables locales
• Comprendre la notion de transmission d’arguments entre fonctions
• Connaître les classes d’allocation des variables
•Définir la portée d’une variable
Eléments de contenu
• La notion de fonction en C
• Exemple de fonction en C
• Les arguments d’une fonction
• Les fonctions fournissant un résultat
• Les prototypes et le type void
• Les arguments sont transmis par valeurs
• Les variables globales
• Les variables locales
• La compilation séparée et ses conséquences
• Les classes d’allocation des variables- leur portée et leur initialisation
Comme tous les langages, C permet de découper un programmes en plusieurs parties nommées
souvent “modules”. Cette programmation dite “modulaire’’ se justifie pour plusieurs raisons:
X
by Counterflix
• Un programme écrit en un seul module devient difficile à comprendre dès qu’il dépasse une
ou deux pages de texte. Une écriture modulaire permet de le diviser en plusieurs parties.
Chacune de Ces parties peut d’ailleurs, si nécessaire, être décomposée à son tour en des
modules plus élémentaires; Ce processus de décomposition pouvant être répété autant de fois
que nécessaire comme le préconisent les méthodes de programmation structurée.
• La programmation modulaire permet d’éviter des séquences d’instructions répétitives, et
ceci d’autant plus que la notion “d’argument” permet de “paramétrer” certains modules.
• La programmation structurée permet le partage d’outils communs qu’il suffit d’avoir écrit
et mis au point une seule fois. Cet aspect sera plus visible grâce â la possibilité de compilation
séparée offerte par le langage C.
I-LA NOTION DE FONCTION EN C
Dans beaucoup de langages, on trouve deux sortes de modules :
• Les “fonctions”, assez proches de la notion mathématique correspondante. Notamment, une
fonction dispose d’arguments qui correspondent à des informations qui lui sont transmises et
elle fournit un unique résultat scalaire simple désigné parle nom même de la fonction, ce
dernier peut apparaître dans une expression.
• Les “procédures” (terme Pascal) ou “sous-programme” qui élargissent la notion de
fonction. La procédure ne possède pas de valeur et son appel ne peut pas apparaître au sein
d’une expression quelconque. Par contre, elle dispose d’argument. Parmi ces derniers certains
peuvent, comme pour la fonction, correspondre à des informations qui lui sont transmises. Mais
d’autres, contrairement à ce qui se passe pour les fonction correspondent à des informations
qu’elle produit en retour de son appel.
En C, il n’existe qu’une seule sorte de module: la fonction. On l’utilise à la manière de la
fonction mathématique, en faisant suivre son nom d’une liste d’arguments; le tout possède une
valeur et peut donc apparaître dans une expression quelconque. Mais cette valeur peut ne pas
être utilisée ; c’est ce qui se passe fréquemment lorsque vous utilisez printf ou scanf.
D’autre part, en C, une fonction peut modifier certains de ses arguments et donc fournir des
informations en retour. Ainsi, malgré son nom, en C, la fonction peut jouer un rôle aussi
général que la procédure ou le sous-programme d’autres langages.
Par ailleurs, nous verrons qu’en C plusieurs fonctions peuvent partager des informations
autrement que par passage d’arguments, Nous retrouverons la notion classique de “variable
globale”.
Il- EXEMPLE DE FONCTION EN C
Voici un exemple très simple de définition et d’utilisation de fonction en C
nain()
{
ptimiste () il fait beau
}
optimiste()
{
printif (“Il fait beau“);
}
Nous y trouvons tout d’abord un programme principal formé d’un bloc (limité ici à une seule
instruction). Mais cette fois à sa suite apparaît la définition d’une fonction. Celle-ci comporte :
• un “en-tête“ qui précise le nom de la fonction (ici optimiste). Bien que cette fonction ne
comporte aucun argument, les parenthèses qui suivent son nom sont obligatoires (comme elles
l’étaient pour le nom main). Notez bien qu’un point-virgule n’apparaît pas à la fin de l’en-tête.
• un “corps” matérialisé par un bloc dans Lequel se trouvent les instruction que l’on souhaite
voir exécutées par la fonction lorsqu’elle sera “appelée”
Ici, nous avons placé la fonction à la suite du programme principal, mais nous aurions pu la
placer avant.
D’autre part, il faut noter que les fonctions sont toujours indépendantes les unes des autres.
Aucune fonction ne peut être contenue dans une autre.
Au sein du programme principal, nous trouvons l’appel de la fonction. Il se fait par son nom
suivi d’une paire de parenthèses obligatoires (même lorsque la fonction ne comporte aucun
argument).
Vous voyez que le programme principal apparaît finalement comme une fonction de nom
(main) imposé. La seule différence est que, par convention, l’exécution d’un programme
commence toujours par cette fonction nommée main. Cette dernière n’est donc exécutée qu’une
seule fois, alors que les autres fonctions peuvent l’être un nombre quelconque de fois.
III- LES ARGUMENTS D’UNE FONCTION
Soit l’exemple suivant :
main ()
{
int a =10, b=20;
ecris(a) ; valeur: 10
ecris(b) ; valeur: 20
ecris(a+b) ; valeur: .30
}
ecris( int n)
{
printf (“valeur :%d\n“ ,n) ;
}
Dans la définition de la fonction ecris, nous trouvons l’en-tête :
ecris(int .n)
Cette fois, en pLus du nom de la fonction, nous y trouvons une indication signifiant que cette
fonction possède un argument de type int nommé n. Cela signifie que lorsque cette fonction
sera appelée, on lui transmettra une valeur de type int. Quant à l’usage que ecris doit faire de
cette valeur, celui-ci lui est spécifié au sein des instructions du corps de la fonction, sachant que
c est l’identificateur n qui désigne la valeur en question.
Le symbole n n’a de signification qu’au sein de la fonction ecris et il n’a aucun rapport avec
d’éventuelles variables de même nom qui pourraient être définis en dehors. On dit souvent que
n est un argument muet ou encore un argument formel.
Eu ce qui concerne l’utilisation de notre fonction ecris dans le programme principal, vous
constatez que nous faisons suivre son nom d’une expression de type int, placée entre
parenthèses. Cette expression porte je nom d’argument effectif .C’est sa valeur qui sera
effectivement transmise à la fonction lors de l’appel.
En C, un argument effectif porte la forme de n’importe quelle expression. Au contraire un
argument muet ne peut être qu’un identificateur de variable
D’une façon générale, une fonction peut avoir plusieurs arguments séparés par des virgules.
Voici un exemple incomplet de définition et d’utilisation d’une fonction nommée fct possédant
trois arguments.
main
{
int n, p ;
double x,y;
…
fct (n, p,x) ;
…
fct (n+p, 5 *n,x-y);
…
}
fct (int i, int j, double v)
{
…
}
Dans ce cas, la correspondance entre argument effectifs et argument formels se fait en fonction
de leur ordre d’apparition dans la liste.
Remarque:
Dans le C tel qu’il est défini par K&R, la déclaration de notre fonction ecris se présente de la
façon suivante :
ecris (n)
int n ;
{
…
}
L’entête est découpée en deux parties :
• une première ligne précisant le nom de la fonction et la liste des arguments formels.
• une suite de déclarations précisant les types des arguments.
IV- LES FONCTIONS FOURNISSANT UN RESULTAT
IV .1 Exemple de fonction calculant la somme de deux valeurs
soit cet exemple :
main()
{
int a, b, c, d, x, y;
int som() ;
a=1; b=2; c=S; d=4;
x = som(a,b)+ ; x=8
printf( “r = %d\n”,x); y = 21
y = 3*som(c,d) ;
printf( “y =%d\n“,y) ;
}
int som (int u,int vi)
{
int s;
s = u+v
return(s)
}
L’entête de la fonction précise toujours la liste des arguments formels avec leurs types ; mais de
plus, ici, on y trouve (au début) l’indication du type du résultat que fournira la fonction.
Par ailleurs, dans le corps de la fonction on trouve une instruction :
return (s);
qui spécifie le résultat qui sera fournit par la fonction lors de son appel.
L’utilisation de la fonction som au sein du programme se fait toujours en faisant suivre son nom
d’une liste d’arguments effectifs (ici deux arguments), comme par exemple :
som (a,b)
Comme la fonction fournit un résultat, une telle notation désigne effectivement une expression
qu’il est possible de l’utiliser à son tour dans une expression plus complète, comme nous
l’avons fait dans l’expression :
x= som (a, b) + 5;
De plus vous constatez la présence dans le programme principal d’une déclaration
supplémentaire :
int som();
Cette déclaration précise au compilateur que som est une fonction et qu’elle fournit un résultat
de type int. Nous reviendrons en détail sur la justification de cette déclaration dans la suite de
cette leçon.
IV.2 L’instruction return
D’une manière générale :
• L’instruction return peut mentionner n’importe quelle expression. Ainsi nous aurions pu
écrire la fonction précédente d’une manière plus simple :
int som (int x, int y)
{
return (u+v);
}
• L’instruction returnpeut apparaître à plusieurs reprises dans une fonction, comme dans cet
exemple :
double absom(double u, double v) // va leur absolue de la somme
{
double s;
s = a +b;
if (s> 0)
return (s),
else
return (-s);
}
il faut noter que non seulement l’instruction return définit la valeur du résultat, mais en même
temps elle interrompt l’exécution de la fonction en revenant à la fonction (y compris la fonction
principale main)qui l’a appelée. En l’absence de l’instruction return ce retour est mis en place
automatiquement par le compilateur à la fin de la fonction.
fct1(….)
{
…
}
fct2(….)
{
….
}
Les variables n et x sont accessibles aux fonctions fct1 et fct2, mais pas au programme
principal.
b) Classe d’allocation
D’une manière générale, les variables globales existent pendant toute l’exécution du
programme dans lequel elles apparaissent. Leurs emplacement mémoires sont complètement
définis lors de l’édition des liens. On dit quelles font partie de la “classe d’allocation statique“
De plus, Ces variables sont initialisées à zéro avant le début de l’exécution du programme, saut
bien sûr, si vous les initialisez explicitement lors de leur déclaration.
VIII- LES VARIABLES LOCALES
Les variables définies au sein d’une fonction (qui peut être la fonction main) sont dites locales
à la fonction dans laquelle elles sont déclarées.
VIII. 1 Portée des variables locales
Les variables locales ne sont connues qu’à l’intérieur des fonctions où elles sont déclarées. Leur
portées est donc limitée à cette fonction.
Les variables locales n’ont aucun lien avec des variables globales de même nom ou avec
d’autres variables locales à d’autres fonctions. Voyez cet exemple :
int n ;
main()
{
int p ;
…
}
int n ; p
float x ;
fct(...)
{
int n ;
int p;
…
}
La variable p de main n’a aucun rapport avec la variable p de fct. De même, la variable n de fct
n’a aucun rapport avec la variable globale n. Notez qu’il est impossible d’utiliser, dans la
fonction fct, cette variable globale n.
VIII.2 Les variables locales automatiques
Par défaut, les variables locales ont une “durée de vie” limitée à celle d’une exécution de la
fonction dans laquelle elles figurent.
Plus précisément, leurs emplacements ne sont pas définis de manière permanente comme ceux
des variables globales un nouvel espace mémoire leur est alloué à chaque entrée dans la
fonction et libéré à chaque sortie. Il sera donc généralement différent d’un appel à l’autre. On
dit que la classe d’allocation de ces variables est automatique. Il faut souligner que : les valeurs
des variables locales ne sont pas conservées d’un appel au suivant.
D’autre part, les valeurs transmises à une fonction sont traitées de la même manière que les
variables locales. Leur durée de vie correspond également à celle de la fonction.
VIII.3 Les variables locales statiques
Il est toutefois possible d’attribuer un emplacement permanent à une variable locale et qu’ainsi
sa valeur se conserve d’un appel au suivant. Il suffit pour cela de la déclarer à l’aide du mot-
clé :
Static
En voici un exemple :
main()
{
void fct(); appel n°1
int n ; appel n0 2
for( n=1 ; n<=5 ;n++) appel n0 3
appel n0 4
appel n0 5
fct() ;
void fct()
{
static int i;
i++;
printf(“appel n° %d\n“ ,i) ;
}
La variable locale i a été déclarée de classe “statique”, On constate bien que sa valeur progresse
de un à chaque appel. De plus on note qu’au premier appel sa valeur est nulle. En effet, comme
pour les variables globales (lesquelles sont aussi de classe statique) :
les variables locales de classe statique sont, par défaut initialisée à zéro.
Remarque:
Le mot staticemployé sans indication de type est équivalent à static int.
VIll.4Le cas des fonctions récursives
Le langage C autorise la récursivité des appel des fonctions. Celle-ci peut prendre deux aspects:
• récursivité directe :une fonction comporte, dans sa définition, au mois un appel à elle
même.
• récursivité croisée : l’appel d’une fonction entraîne celui d’une autre fonction qui, à son
tour appelle la fonction initiale ( on peut faire intervenir plus de deux fonctions).
Voici un exemple classique d’une fonction calculant le produit factoriel d’un nombre d’une
manière récursive :
long fact (int n)
{
if(n > 1) return(fact( n - 1) * n);
e/se return(1) ;
}
Il faut bien voir qu’alors chaque appel de fact entraîne une allocation d’espace pour les
éventuelles variables locales et pour son argument. Or, chaque nouvel appel de fact “à
l’intérieur” de fact provoque une telle allocation sans que les emplacements précédents ne
soient libérés. Il y a donc empilement des appels de la fonction. Ce n’est que lors de l’exécution
de la première instruction return que l’on commence à ‘‘dépiler” les appels et les
empLacements et donc à libérer de l’espace mémoire.
IX- LA COMPILATION SEPAREE ET SES CONSEQUENcES
C permet de compiler séparément plusieurs fichiers sources et de rassembler les
modules objets correspondants au moment de l’édition des liens.
IX.1 La portée d’une variable globale - la déclaration extern
A priori, la portée d’une variable globale semble limitée au fichier source dans lequel elle a été
définie. Ainsi, supposez que l’on compile séparément ces deux fichiers sources
source I source 2
int x; fct2()
main() {
{ ….
}
….
} fct3()
fct() {
….
}
{
….
}
A priori, il ne semble pas possible, dans les fonctions fct2 et fct3 de faire référence à la variable
globale x déclarée dans le premier fichier source (alors qu’aucun problème ne se poserait si l’on
réunissait ces deux fichiers sources en un seul, du moins si l’on prend soin de placer les
instructions du second fichier à la suite de celles du premier).
En effet, le langage C prévoit une déclaration permettant de spécifier qu’une variable globale a
déjà été déclarée dans un autre fichier source. Celle-ci se fait à l’aide du mot-clé extern.. Ainsi
en faisant précéder notre second fichier source de la déclaration
extern int x;
il devient possible de mentionner la variable globale x (déclarée dans le premier fichier source)
dans les fonctions fct2 etfct3.
IX.2 Les variables globales et l’édition des liens
Supposons que nous avons compilé les deux fichiers sources précédents, après avoir introduit
dans le second la déclaration extern dont nous avons parlé ; voyons alors comment l’éditeur de
lien est en mesure de rassembler correctement les deux modules objets ainsi obtenus. En
particulier, examinons comment il fait correspondre au symbole x du second fichier source
l’adresse effective de la variable x définie dans le premier.
D’une part, après compilation du premier fichier source, on trouve dans le
module objet correspondant, une indication associant le symbole x et son adresse dans le
module objet. Autrement dit, contrairement à ce qui se passe pour les variables locales pour
lesquelles ne subsiste aucune trace de nom après compilation, le nom des variables globales
continue à exister dans les modules objets. On retrouve là un mécanisme analogue à ce qui se
passe pour les noms de fonctions, lesquels doivent bien subsister pour que l’éditeur de lien soit
en mesure de retrouver les modules objets correspondants.
D’autre part, après compilation du second fichier source, on trouve dans le
module objet correspondant une indication mentionnant qu’une certaine variable de nom x
provient de l’extérieur et qu’il faudra en fournir l’adresse effective.
Ce sera effectivement le rôle de l’éditeur de liens que de retrouver dans le premier
module objet l’adresse effective de la variable x et de la reporter dans le second module objet.
X- LES CLASSES D’ALLOCATION DES VARIABLES - LEUR PORTEE ET LEUR
INITIALISATION
X.1 La portée des variables
On peut classer les variables en quatre catégories en fonction de leur portée (ou espace de
validité).
a) Les variables globales
Elles sont accessibles depuis n’importe quel endroit du fichier source faisant suite à
l’emplacement où elles ont été définies. Elles sont également accessible depuis un autre fichier
source ou depuis une autre partie du fichier source qui précède l’endroit où elles sont définies,
par l’intermédiaire de la déclaration extern.
b) Les variables globales cachées
Ce sont des variables globales faisant l’objet d’une déclaration static. Elles ne sont accessible
que depuis le fichier source où elles ont été définies.
c) Les variables locales à une fonction
N’oubliez pas que les variables locales au programme principal (main) entrent dans cette
catégorie.
d) Les variables locales à un bloc
Le langage C permet de déclarer des variables au début d’un bloc de la même façon qu’au
début d’une fonction. Dans ce cas la portée de telles variables est limitée au bloc en question.
X.2 Les classes d’allocation des variables
Il est également possible de classer les variables en trois catégories en fonction de leur classe
d’allocation
a) La classe stade
On trouve dans cette catégorie les variables globales et les variables locales faisant l’objet
d’une déclaration static. Les emplacements mémoire correspondants sont alloués une fois pour
toutes au moment de l’édition de lien.
b) La classe automatique
Par défaut les variables locales entrent dans cette catégorie. Les emplacements mémoire
correspondants sont alloués à chaque entrée dans la fonction et ils sont libérés à chaque sortie.
c) La classe register
Toute variable entrant à priori dans la classe automatique peut être déclarée explicitement par le
qualificatif register. Celui-ci demande au compilateur d’utiliser dans la mesure du possible, un
registre pour y ranger la variable ceci peut amener quelques gains de temps d’exécution.
Cette possibilité ne peut s’appliquer qu’aux variables scalaires.
X3.Le cas des fonctions
La fonction est considérée par le langage C comme un “objet global”. c’est ce qui permet
d’ailleurs à l’éditeur de lien d’effectuer correctement son travail. Il n’est pas nécessaire
d’utiliser une déclaration extern pour les fonction définies dans un fichier source différent de
celui où elles sont appelées.
X.4 Initialisation des variables
a) Les variables de classe statique
Ces variables sont permanentes. Elles sont initialisées une seule fois avant le début de
l’exécution du programme.
Elles peuvent être initialisées explicitement lors de leur déclaration. Les valeurs servant à cette
initialisation ne peuvent être que des constantes. En l’absence d’initialisation explicite, Ces
variables seront initialisées à’’zéro ’’.
b) Les variables de classe automatique
Ces variables ne sont pas initialisées par défaut. Par contre, comme les variables de classe
statique, elles peuvent être initialisées explicitement lors de leur déclaration.
LES TABLEAUX ET LES POINTEURS
Objectifs
• comprendre la non on de tableau en langage c
• connaître la syntaxe de déclaration des tableaux à une dimension et à plusieurs dimensions
• Comprendre la notion pointeur en langage C
•Identifier le lien entre les pointeurs et les tableaux
• Appliquer l’utilisation des pointeurs en argument d’une fonction
Eléments de contenu
• Les tableaux à une dimension
• Les tableaux a plusieurs dimension
• Classe d’allocation des tableaux et initialisation
•Notion de pointeurs - Les opérateur * et &
• Utilisation des pointeurs en argument d’une fonction
• Lien entre les pointeurs et les tableaux
•Les tableaux transmis en arguments d’une fonction
Comme tous les langages, C permet d’utiliser des tableaux. On nomme ainsi Un ensemble
d’éléments de même type désignés par un identificateur unique chaque élément est repéré par
un indice précisant Sa position au sein de l’ensemble
X
by Counterflix
Par ailleurs, comme certains langages, C dispose (le ‘pointeurs”, c’est à dire des variables
destinées à contenir des adresses d’autres “objets” (variables, fonctions,...).
A priori, ces deux notions de tableau et de pointeur peuvent paraître éloignées l’une de ‘autre.
Toutefois, il se trouve qu’en C un lien indirect existe entre les deux notions, à savoir qu’un
identificateur de tableau est un constant pointeur.
I-LES TABLEAUX A UNE DIMENSION
Supposons que nous souhaitions déterminer, à partir de 20 notes d’élèves
(fournies on données), combien d’entre elles sont supérieures à la moyenne de la classe.
S’il ne s’agissait que de calculer simplement la moyenne de ces notes, il nous. suffirait d’en
calculer la somme on les cumulant dans une variable, au fur et à mesure de leur lecture. Mais,
ici, il nous faut à nouveau pouvoir consulter Les notes pour déterminer combien d’entre elles
sont supérieures à la moyenne ainsi obtenue, il est donc nécessaire de mémoriser ces 20 notes.
Pour ce faire, il paraît peu raisonnable de prévoir 20 variables scalaires (méthode qui serait.
Difficilement transposable à un nombre important de notes).
Le tableau va nous offrir une solution convenable à ce problème, comme le montre le
programme suivant
Main()
{
int i,som,nbm ;
float moy;
int t[20] ;
for (i=0; i<20;I++)
{
printf( “donner la note numéro %d :’’,i+1’’)
scanf(‘’%d’’,&t[i]) ;
}
som=0;
for(I=0; i< 20 ;i++)som+=t[i] ;
moy=som\20;
printf,(‘’\n\n La moyenne de cette classe est :%f\n’’,som) ;
nbm=0 ;
for(i=0 ;i<20 ;i++)
if(t[i]>moy) nbm++;
printf(‘’%d élève ont plus de cette moyenne \n’’, nbm);
}
La déclaration :
int t [20]
réserve emplacement pour 20 élément de type int , Chaque élément est repéré par Sa position
dans le tableau ,nommée ‘‘indice”. Conventionnellement, en langage C, la première position
porte le numéro 0. Ici, donc, nos indices vont de O à 19, le premier élément du tableau sera
désigné par t[0], le troisième par t[2], le dernier par t[l9].
Plus généralement, la notion t[i] désigne un élément dont la position dans le tableau est fournie
parla valeur de i. Elle joue le même rôle qu’une variable scalaire de type int.
La notation &t[i] désigne l’adresse de cet élément t[i] de même que &n désigne l’adresse de n
D’une façon générale
1) Un élément d’un tableau est une ivalue. Il peut donc apparaître à gauche d’un opérateur
d’affectation comme dans :
t[2] = 5
Il peut aussi apparaître comme opérande d’un opérateur d’incrémentation, comme dans :
t[3]++ - -t[i]
2) L’indice peut prendre la forme de n’importe quelle expression arithmétique de type entier
(ou caractère compte tenu des règle de conversion implicite). Par exemple, si n, p, k, et j sont de
type int Ces notations sont correctes :
t[n-3]
t[3*p-2*k+j%n]
Il en va de même si c1 et c2 de type char, de :
t [c1+3]
t[c2-c1]
3) Aucun contrôle de “débordement d’indice’’ n’est, mis en place par le compilateur. Pour en
comprendre les conséquences, il faut savoir que lorsque le compilateur rencontre
une L’aine telle que t[i], il en détermine l’adresse en ajoutant à l’adresse de début t, un
décaLage proportionnel à. la valeur de i (et aussi proportionnel à la taille de chaque élément du
tableau). De sorte qui est très facile de désigner et de modifier un emplacement situé avant ou
après le tableau.
Il- LES TABLEAUX A PLUSIEURS DIMENSIONS
Comme tous les langages, C autorise les tableaux à plusieurs indices (ou à plusieurs
dimensions).
Par exemple, la déclaration :
int t[5][3] ;
réserve un tableau de 15(5*3) éléments. Un élément quelconque de ce tableau se trouve alors
repéré par deux indices comme dans ces notations :
t[3][2] t[i] [j] t[i-3][i+j]
il faut noter que là encore, la notation désignant un élément d’un tel tableau est
En langage C l’identificateur d’un tableau, lorsqu’il est employé seul (sans indice à sa suite)
désigne on fait adresse de début de ce tableau, c’est à dire l’adresse de son premier élément.
Objectifs
• Connaître la convention de représentation des chaînes de caractères en C
• Comprendre les entrées -sorties de chaînes
• Manipuler les fonctions de traitement et de conversion
Elément de contenu
• La convention de représentation de chaînes de caractères en C
• Les entrées sorties de chaînes
• Les fonctions de concaténation de chaînes
• Les fonctions de comparaison de chaînes
• Les fonctions de copie de chaînes
• Les fonctions de recherche dans une chaîne
• Les fonctions de conversion
Certains langages (tels que le Basic ou le Turbo Pascal) disposent d’un véritable “type
chaînes’’. Les variables d’un tel type sont destinées à recevoir des suites de caractères qui
peuvent évoluer, à la fois en contenue et en longueur, au fil du déroulement du programme.
Elles peuvent être manipulées d’une manière globale.
X
by Counterflix
D’autres langages (tels que Fortran ou le Pascal standard) ne dispose pas d’un tel type chaîne.
Pour traiter de telles informations, il est nécessaire de travailler sur des tableaux de caractères,
dont la taille est nécessairement fixe (ce qui impose une longueur maximum aux chaînes et
entraîne donc une perte de l ‘emplacement mémoire).
En langage C, il l’existe pas de véritable type chaîne, dans la mesure où on ne peut pas déclarer
des variables de ce type. Par contre, il existe une convention de représentation des chaînes.
Celle-ci est utilisée à la fois :
• par te compilateur pour représenter les constantes chaînes (notées entre double quotes),
• par un certain nombre de fonctions qui permettent de réaliser
- les lectures ou écritures de chaînes,
- les traitements classiques tels que la concaténation, recopie, comparaison, extraction
de sous-chaîne, conversions,….
Mais, comme il n’existe pas de type chaîne, il faudra prévoir un emplacement pour accueillir
ces informations. Un tableau de caractères pourra faire l’affaire a c’est ce que nous allons
utiliser dans ce chapitre. Mais nous verrons plus tard comment créer dynamiquement. des
emplacements mémoires, lesquels seront alors repérés par des pointeurs.
1- LA CONVENTION DE REPRESENTATION DES CHAINES EN C
En C, une chaîne de caractères est représentée par une suite d’octets correspondant à chacun de
ses caractères codés en ASCII, le tout étant terminé par un octet supplémentaire de code nul
cela signifie que, d’une manière générale, une chaîne de n caractères occupe en mémoire un
emplacement de n +1 octets.
1.1 Les constantes chaînes de caractères
C’est cette convention qu’utilise le compilateur pour représenter les constantes chaînes que
vous introduisez dans vos programmes, sous des notations de la forme :
“bonjour“
De plus, une tel notation sera traduite par le compilateur on un pointeur sur des éléments de
type char sur la zone mémoire correspondante.
Voici un programme illustrant ces deux particularités :
main()
{
char * adr ;
adr=“bonjour“ ;
do
printf(“%c“,*adr) ;
while(*adr++);
}
La déclaration :
char *adr
réserve un emplacement pour un pointeur sur un caractère (ou une suite de caractères)En ce qui
concerne la constante :
‘‘bonjour’’
le compilateur a créé en mémoire la suite d’octets correspondants mais, dans l’affectation
adr- = “bonjour”
la notation bonjour a pour valeur, non pas la valeur de la chaîne elle même, mais son adresse ;
on retrouve ici le même phénomène que pour les tableaux.
Voici un schéma illustrant la situation après l’exécution de cette affectation
b o n j o u r \o
adr
1.2 Initialisation de tableaux de caractères
Comme on l’a dit, nous sommes souvent amenés, en C, a placer des chaînes dans des tableaux
de caractères.
Mais, si on déclare, par exemple :
char ch[20] ;
On ne pourra pas transférer une chaîne constante dans ch en écrivant une affectation du genre :
ch =” bonjour”;
En effet, ch est une constante pointeur et non une ivalue ; il n’est donc pas possible de lui
attribuer une valeur.
Par contre, C nous autorise a initialiser notre tableau de caractères à l’aide dune chaîne
constante. Ainsi, on pourra écrire :
char ch[20] =’’bonjour’’
Ceci sera parfaitement équivalent à une initialisation de ch réalisée par une énumération de
caractères (sans oublier le code 0 noté \0) :
char ch[20]={‘b’,’o’ ,’n’,’j’,’o’,’u’,’r’,’\0’} ;
I.3 Initialisation de tableaux de pointeurs sur des chaînes
Nous avons vu qu’une constante chaîne de caractères était traduite par le compilateur en une
adresse que l’on pouvait, par exemple, affecter à un pointeur sur une chaîne. Cela peut se
généraliser à un tableau de pointeurs, comme dans :
char * jour [7] ={ “lundi”, “mardi”, “mercredi “, “jeudi”,
‘‘Vendredi’’, “ samedi’’, “ dimanche’’} ;
Cette déclaration réalise donc, à la fois
- la création des 7 constantes chaînes correspondant aux 7 jours de la semaine,
- l’initialisation du tableau jour avec les 7 adresses de ces 7 chaînes.
Voici un exemple utilisation cette déclaration :
main()
{
char *jour [7] = { “lundi “, “mardi “, “mercredi “, “jeudi
‘‘vendredi “, “samedi “, ‘‘dimanche ‘‘) ;
int i ;
printf(’’donnez un entier 1 et 7 :’’)
scanf(”%d’’, &i) ;
printf (“le jour n° %d de la semaine est %s”, i, jour[i-1]) ;
}
donnez un entier 1 et 7 :3
le jour n° 3 de la semaine est mercredi
Il- LES ENTREES-SORTIE DE CHAINES
Le langage C offre plusieurs possibilités de lecture ou d’écriture de chaînes
• l’utilisation du code format %s dans les fonctions printf et scanf ou (esscanf)
• les fonctions spécifiques de lecture (gels) ou d’affichage (puts) d’une chaîne (une seule à la
fois),
• a fonction de lecture directe au clavier (cgets) qui présente l’avantage d’autoriser un
contrôle de la taille de la chaîne lue et, partant, de vous protéger contre un éventuel risque de
débordement.
Ici nous nous limitons aux entrées-sorties conversationnelles les autres possibilités seront
examinées lors de l’étude des fichiers.
II.1 Les fonctions gets, puts et le code format %s
Examinons cet exemple de programme :
main()
{
char nom [ 20], prénom[2 0], ville[25] ;
printf(’’quelle est votre ville :’’) ;
gets(ville) ;
printf(’’donnez votre nom et votre prénom :’’) ;
scanf(‘’%s%s’’, nom ,prénom) ;
printf(‘’bonjour cher %s %s qui habite à ‘’,nom ,prénom) ;
puts(ville) ;
}
Quelle est votre ville : Tunis
Donnez votre nom et votre prénom : Foulen ben Foulen
Bonjour cher Foulen ben Foulen qui habite à Tunis
Les fonctions printfet sarnf permettent de lire et d’afficher simultanément plusieurs
informations le type quelconque. Par contre gets et puts ne traitent qu’une chaîne à la fois.
De plus, les délimitations de la chaîne lue ne s’effectuent pas de la même façon avec scanf et
gets. Plus précisément :
· avec le code %s de scanf les délimiteurs sont classiquement l’espace, la tabulation ou la fin de
ligne. Ceci interdit la lecture d’une chaîne contenant des espaces.
· avec gets, seule la fin de ligne sert de délimiteur.
Dans tous les cas, on remarque que la lecture de n caractères implique le stockage on mémoire
de n+1 caractères car le caractère de fin de chaîne (\0) est généré automatiquement par les
fonctions de lecture.
Ainsi, dans notre précédent programme, il n’est pas souhaitable que le nom fourni en donnée
contienne plus de 19 caractères.
Remarques :
1) Dans les appels des fonctions scanf et puts, les identificateurs de tableaux comme nom,
prénom ou ville ne doivent pas être précédés de l’opérateur & puisqu’ils représentent déjà des
adresse.
2) La fonction gets fournit en résultat, soit un pointeur sur la chaîne lue, soit un pointeur nul on
cas d’anomalie.
3) La fonction puts réalise un changement de ligne en fin de l’affichage de la chaîne, ce qui
n’est pas le cas de la fonction printf avec le code format %s.
Les fonctions cgets et sscanf
Le problème de risque de débordement de l’espace alloué à une chaîne lue on donnée peut être
résolue par l’utilisation de la fonction cgets à la place de gets. Celle-ci permet d’imposer un
nombre maximum de caractères à lire.
Voici un exemple qui lit une chaîne comportant au maximum 27 caractères (28 avec le
caractère nul de fin de chaîne).
# inc/ude <stdio h>
main()
{
char texte[30];
printf(‘’donnez un texte : ‘’) ;
texte [0]=28 ;
cgets(texte) ;
printf(‘’vous avez fini %d caractères \n ‘’,texte[1]) ;
puts(&texte[2]= ;
}
Donnez un texte :bonjour
Vous avez fini 7caractères
Bonjours
Donnez un texte abcdefghijklmnopqrstuvwxyza
Vous avez fourni 27 caractères abcdefghijklmnopqrstuvwxyza
En fait cgets utilise les deux premiers caractères de la “zone” dont on lui fournit l’adresse pour
gérer sa lecture
· le premier octet doit être initialisé par le programme au nombre maximum de caractères
que l’on souhaite ranger en mémoire, y compris le caractère nul de fin
· le second octet sera renseigné par cgets à la fin de l’opération pour indiquer le nombre de
caractères effectivement lus.
Cette fonction ne lira pas plus de caractères que prévu. Si l’utilisateur cherche à en fournir
davantage, il en sera informé par l’émission d’un bip sonore. Notez que, dans tous les cas, il
doit valider sa réponse par la touche return.
Nous avons vu les problèmes posés par scanf on cas de réponse incorrecte de la part de
l’utilisateur.
Il est possible de régler la plupart de ces problèmes on travaillant en deux temps
• lecture d’une chaîne de caractères par cgets
• vérification du format de manière comparable à ce que ferait scanf.
Voici un exemple de programme permettant de questionner l’utilisateur jusqu’à ce qu’il ait
fournit une réponse satisfaisante :
# define LG 80
# def;ne LG3 LG+3
#include <stdio.h>
main()
{
int n, compte;
char c;
char tampon[LG3] ;
tampon[0]=LG+1;
do
{
printf(“Donncz un entier et un caractère :’’) ;
cgcts tampon() -
compte = sscanf(&tanpon[2],%d %c”, &n, &c,);
} while (compte <2) ;
printf <“merci pour, ‘‘%d %c “, n, c) ;
}
Donnez un enter et un caractère : bof
Donnez un entier et un caractère : a 125
Donnez un entier et un caractère : 12 bonjour
Merci pour 12 b
III- LES PONCTIONS DE CONCATENATION DE CHAINES
La concaténation est la juxtaposition de chaînes afin d’en former une seule. Elle est réalisée en
C à l’aide de l ‘une des deux fonctions strcat et strncat (dont les prototypes figurent dans string.
h)
111.1 La fonction strcat
Examinons cet exemple :
#include <string.h>
main()
{
char ch l[50]=’’bonjour’’ ; avant bonjour
char *0h2=’’monsieur”; après : bonjour monsieur:
printf (“avant: %s\n “, ch1);
strcat(ch 1, ch2);
printf (“après: %s\n’’,ch 1);
}
Il faut noter la différence entre les deux déclarations (avec initialisation) de chacune des deux
chaînes ch1 et ch 2. La première permet de réserver un emplacement plus grand que la
constante chaîne qu’y trouve place initialement.
L’appel de strcat se présente ainsi :
strat (destination, source)
Cette fonction recopie [a seconde chaîne source à la suite de a première destination, après en
avoir effacé le caractère de fin.
Remarque :
1) Aucun contrôle de longueur n’est réalisé par cette fonction ;il est nécessaire que
emplacement réservé pour la première chaîne soit suffisant pour y recevoir la partie à lui
concaténer.
2) Après exécution de srtcat, la première chaîne n’existe p\us en tant que telle.
3) strcat fournit un résultat
- l’adresse de la chaîne correspondant à la concaténation des deux chaînes fournies en
argument, lorsque l’opération s’est bien déroulée. Ce n’est rien d’autre que l’adresse de ch1.
Le pointeur nul lorsque l’opération s’est mal déroulée.
II.2 La fonction strncat
Cette fonction, dont l’appel se présente ainsi :
strcat (destination, source, igmax)
Travail le de façon semblable a strcat en offrant on outre un contrôle sur le nombre de
caractères qui seront concaténés à la chaîne d’arrivée (destination).
#include <string. h>
main()
{
char ch[50] = “bonjour’’ ; avant :bonjour
char *ch2 = “ monsieur’; après :bonjour monsieur
printf(’’avant %s\n’’;ch1);
strncat (ch1,ch2,6);;
printf(’’apres:%s\n’’;ch1);
}
Il faut noter que le contrôle ne porte pas sur la longueur de la chaîne finale. Fréquemment on
déterminera ce nombre maximal de caractères à recopier comme étant la différence entre la
taille totale de la zone réceptrice et la longueur courante de la chaîne qui s’y trouve. Cette
dernière s’obtiendra par la fonction strlen. Par exemple :
strlen(ch1)
Fournit une valeur de type int correspondant à la longueur de la chaîne ch1. Le caractère nul de
fin, n’étant pas comptabilisé.
IV- LES FONCTIONS DE COMPARAISON DE CHAINES
Il est possible de comparer deux chaînes en utilisant l’ordre des caractères défini par le code
ASCII.
a) La fonction :
strcmp (ch1, ch2) (string. h)
compare deux chaînes dont on lui fournit les adresses et elle fournit une valeur entière définie
comme étant :
· positive si ch1 > ch2 (c’est à dire chl vient après ch2 au sens de l’ordre défini par le code
ASCII)
· nulle si ch1 = ch2 (c’est à dire que chl et ch2 contiennent exactement la même suite de
caractères).
· négative si ch 1 < ch2
Par exemple :
strcmp(”bonjour “, “monsieur’’)
est négatif et
strcmp(”Tunis2 “, ‘Tunis10’’)
est positif.
b) La fonction
strncmp (ch,ch2,lgmax) (string.h)
Travaille comme strcmop mais elle limite la comparaison au nombre maximum de caractères
indiqué par l’enlier lgmax.
Par exemple :
strncmp(’’bonjour “, ‘‘bon’’, 4)
est positif tandis que
strcmp(’’bonjour “, ’’bon “, 2)
vaut zéro.
c) Enfin, les deux fonctions
stricmp (ch1, ch2) (string.h)
strnicmp (ch1, ch2, lgmax) (string.h)
Travaillent respectivement comme strcmp et strncmp, mais sans tenir compte de la différence
entre majuscules et minuscules (pour les seuls caractères alphabétiques).
V- LES FONCTIONS DE COPIE DE CHAINES
a) La fonction :
strcpy (destin, source) (string.h)
Recopie la chaîne située à l’adresse source dans l’emplacement d’adresse destin. Là aussi, il
faut que la taille du second emplacement soit suffisante pour accueillir la chaîne à recopier.
b) La fonction :
strncpy (destin, source, lgmax,) (string.h)
Procède de manière analogue à strcpy, en faisant la recopie au nombre de caractères précisés
par l’expression entière lgmax.
Il faut noter que si la longueur de la chaîne source est inférieure à cette longueur maximale son
caractère de fin sera effectivement recopié. Mais dans le cas contraire, il ne le sera pas.
L’exemple suivant illustre les deux situations :
#include <string. h>
main()
{
char ch1 [20] = “xxxxxxxxxxxxxxxxx”; donnez un mot: bon
char ch2[20]; bon
printf(’’donnez un mot:’’);
gets(ch2) ; donnez un mot: bonjour
strncpy(ch1, ch2, 6); bonjourxxxxxxxxxxxx
printf(”%s”, ch1);
}
c) Enfin, la fonction :
strcpy(chaîne) (string.h)
Effectue une recopie de la chaîne dont on lui fournit l’adresse dans un emplacement qu’elle
alloue dynamiquement et dont elle fournit l’adresse on résultat (elle fournit le pointeur nul si
elle n’a pas pu disposer de suffisamment de place mémoire.)
VI- LES FONCTIONS DE RECHERCHE DANS UNE CHAINE
On trouve, on langage C, des fonctions de recherche de “l’occurrence’’ dans une chaîne d’un
caractère ou d’une autre chaîne (nommée alors sous-chaîne).
Ces fonctions fournissent comme résultat l’adresse de l’information cherchée on cas de succès
et le pointeur nul dans le cas contraire.
strchr (chaîne, caractère) (string.h)
Recherche, dans chaîne la première position où apparaît le caractère mentionné
strchr (chaîne, caractère) (string.h)
Réalise le même traitement que strchr, mais en explorant la chaîne mentionné à partir de la fin.
Elle fournit donc la dernière occurrence du caractère mentionné.
strstr (chaîne, sous-chaîne) (string.h)
Recherche dans chaîne la première occurrence complète de la sous-chaîne mentionnée.
strpbrk (chaîne 1, chaine2) (string. h)
Recherche, dans chaîne1 la première occurrence d’un caractère quelconque de chaine2.
Voici un programme illustrant le fonctionnement de ces quatre fonctions :
#înc/ude <string.h>
#define c ’e’
#define s ch ‘re’
#define voy ‘aeiou’
main()
{
char ,mot[40];
char *adr ;
printf(’’donnez un mots:);
gets (mot);
if (adr=strchr(mot,c))
printf(’’première occurrence de%c en %s\n’’,c,adr);
if (adr=strrchr(mot,c))
printf(’’dernière occurrence de %c en %s\n’’,c, adr) ;
if (adr = strstr(mot,sch))
printf (“première occurence de %s en %s\n’’;sch,adr) ;
if (adr =strpbrk(mot,voy))
printf (“première occurrence de l’une des lettres de %s en %s\n’’,voy,adr) ;
}
Donnez un mot : correspondance
Première occurrence de e en espondances
Dernière occurrence de e en es
Première occurrence de re en repondances
Première occurrence de l’une des lettres de aeiou en orréspondance
Donnez un mot : bonjour
Première occurrence de l’une des lettres de aeiou en onjour