IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Le langage C


précédentsommairesuivant

III. Instructions

III-A. Syntaxe

Dans les descriptions syntaxiques suivantes le suffixe « opt » indique que la formule qu'il qualifie est optionnelle. Une formule avec des points de suspension, comme « élément ... élément », indique un élément pouvant apparaitre un nombre quelconque, éventuellement nul, de fois.

 
Sélectionnez
instruction
	→ instruction-bloc
	→ instruction-expression
	→ instruction-goto
	→ instruction-if
	→ instruction-while
	→ instruction-do
	→ instruction-for
	→ instruction-break
	→ instruction-continue
	→ instruction-switch
	→ instruction-return
	→ instruction-vide
	→ identificateur : instruction
 
Sélectionnez
instruction-bloc
	→ f déclaration ... déclaration
 
Sélectionnez
instruction-expression
	→ expression ;
 
Sélectionnez
instruction-goto
	→ goto identif ;
 
Sélectionnez
instruction-if
	→ if ( expression ) instruction else instruction
	→ if ( expression ) instruction
 
Sélectionnez
instruction-while
	→ while ( expression ) instruction
 
Sélectionnez
instruction-do
	→ do instuction while ( expression ) ;
 
Sélectionnez
instruction-for
	→ for ( expressionopt ; expressionopt ; expressionopt ) instruction
 
Sélectionnez
instruction-break
	→ break ;
 
Sélectionnez
instruction-continue
	→ continue ;
 
Sélectionnez
instruction-switch
	→ switch ( expression ) { instruction-ou-case ... instruction-ou-case }
 
Sélectionnez
instruction-ou-case
	→ case expression-constante : instructionopt
	→ default : instruction
	→ instruction
 
Sélectionnez
instruction-return
	→ return expressionopt ;
 
Sélectionnez
instruction-vide
	→ ;

C et le point-virgule

Comme l'indique la syntaxe de l'instruction-bloc, en C le point-virgule n'est pas un séparateur d'instructions mais un terminateur de certaines instructions. Autrement dit, il appartient à la syntaxe de chaque instruction de préciser si elle doit ou non être terminée par un point-virgule, indépendamment de ce par quoi l'instruction est suivie dans le programme.

L'oubli du point-virgule à la fin d'une instruction qui en requiert un est toujours une erreur, quelle que soit la situation de cette instruction. Un surnombre de points-virgules crée des instructions vides.

III-B. Présentation détaillée des instructions

III-B-1. Blocs

Un bloc est une suite de déclarations et d'instructions encadrée par les deux accolades { et }. Du point de vue de la syntaxe il se comporte comme une instruction unique et peut figurer en tout endroit ou une instruction simple est permise.

Le bloc le plus extérieur d'une fonction et les autres blocs plus profonds ont le même statut. En particulier, quelle que soit sa position dans le programme, un bloc peut comporter ses propres déclarations de variables. Sauf si elle est déclarée extern, une variable définie dans un bloc est locale à ce bloc et donc inconnue à l'extérieur. Dans le bloc ou elle est définie, une telle variable masque, sans le détruire, tout autre objet de même nom connu à l'extérieur du bloc.

Sauf si elles sont qualifiées static (ou extern), de telles variables sont créées et éventuellement initialisées lors de l'activation du bloc ; elles sont détruites dès que le contrôle quitte le bloc. Il ne faut donc pas espérer qu'une telle variable conserve sa valeur entre deux passages dans le bloc. Les variables locales aux blocs permettent d'optimiser la gestion de l'espace local. Par exemple, dans un programme tel que

 
Sélectionnez
if (...) {
	type1 n;
	...
}
else {
	type2 x;
	...
}

les variables n et x n'existeront jamais simultanément ; le compilateur peut donc leur allouer le même emplacement de la mémoire.

III-B-2. Instruction-expression

Format :

 
Sélectionnez
	expression ;

Mais oui, il suffit d'écrire un point-virgule derrière n'importe quelle expression pour en faire une instruction.

Exemples :

 
Sélectionnez
123; 				a
i++; 				b
x = 2 * x + 3; 		c
printf("%d\n", n); 	d

Intuitivement, une instruction-expression représente l'ordre « évaluez cette expression, ensuite oubliez le résultat ». Il est clair que si l'expression n'a pas d'effet de bord, cela n'aura servi à rien (exemple a). L'aspect utile de cette notion est : toute expression avec effet de bord pourra être évaluée uniquement pour son effet de bord (exemple b).

Avec deux cas particuliers très intéressants :
  • puisque l'affectation est une expression, on retrouve bien l'instruction d'affectation, fondamentale dans tous les langages de programmation (exemple c) ;
  • toute fonction peut être appelée « comme une procédure » (exemple 23 d), c'est-à-dire en ignorant la valeur qu'elle rend.

Remarque. Une conséquence regrettable de tout cela est un piège assez vicieux tendu aux pascaliens.
Imaginons que lirecaractere soit le nom d'une fonction sans argument. L'appel correct de cette fonction s'écrit :

 
Sélectionnez
	lirecaractere();

Cependant, puisque le nom d'une fonction est une expression (une constante valant l'adresse de la fonction) et que toute expression suivie d'un point-virgule est une instruction correcte, l'énoncé

 
Sélectionnez
	lirecaractere;

sera trouvé légal par le compilateur. Or cette expression (tout à fait analogue à l'exemple a ci-dessus) ne produit pas l'appel de la fonction et ne traduit donc probablement pas la pensée du programmeur.

 

23On verra le moment venu (cf. section VII.B.5) que printf rend un résultat parfois utile.

III-B-3. Etiquettes et instruction goto

Format :

 
Sélectionnez
	étiquette : instruction

Une étiquette est un identificateur ; elle doit être placée devant une fonction, séparée de celle-ci par un caractère deux points. Elle n'a pas à faire l'objet d'une déclaration explicite ; il suffit de l'écrire devant une instruction pour qu'elle soit automatiquement connue comme un nom à portée locale. Elle est alors utilisable partout dans la fonction ou elle apparait (avant et après l'instruction qu'elle préfixe) et elle reste inconnue en dehors de la fonction.

L'instruction

 
Sélectionnez
goto étiquette ;

transfère le contrôle à l'instruction préfixée par l'étiquette en question.

Théoriquement, tout algorithme peut être programmé sans utiliser l'instruction goto. Dans certains langages comme Pascal, elle est utilisée pour obtenir l'abandon d'une structure de contrôle (exemple : une boucle) depuis l'intérieur de la structure. Un tel emploi de goto est avantageusement remplacé en C par l'utilisation des instructions return, break et continue.

Il est donc rare que l'on ait besoin de l'instruction goto en C. Elle ne se révèle utile que lorsqu'il faut abandonner plusieurs structures de contrôle (if, while, for...) imbriquées les unes dans les autres. Exemple :

 
Sélectionnez
for (i = 0; i < N1; i++) {
	for (j = 0; j <= N2; j++)
		for (k = 0; k <= N2; k++) {
		...
	if (...)
		goto grande_boucle;
	...
}
...
grande_boucle: 	/* ici on a quitté les deux boucles internes (sur j et k) */
... 			/* mais on est toujours dans la boucle la plus externe (sur i) */
}

III-B-4. Instruction if...else...

Formats :

 
Sélectionnez
if (expression)
	instruction1
else
	instruction2

et

 
Sélectionnez
if (expression)
	instruction1

Dans la première forme, expression est évaluée : si elle est vraie (i.e. non nulle) instruction1 est exécutée ; si elle est fausse (nulle) instruction2 est exécutée. Dans la deuxième forme, expression est évaluée : si elle est vraie instruction1 est exécutée ; sinon, rien n'est exécuté.

On notera que l'expression conditionnelle doit figurer entre parenthèses. Celles-ci font partie de la syntaxe du if, non de celle de l'expression.

Lorsque plusieurs instructions if sont imbriquées, il est convenu que chaque else se rapporte au dernier if pour lequel le compilateur a rencontré une condition suivie d'exactement une instruction. Le listing du programme peut (et doit !) traduire cela par une indentation (marge blanche) expressive, mais il ne faut pas oublier que le compilateur ne tient pas compte des marges. Par exemple, le programme suivant est probablement incorrect ; en tout cas, son indentation ne traduit pas convenablement les rapports entre les if et les else :

 
Sélectionnez
if (nombrePersonnes != 0)
	if (nombrePersonnes != nombreAdultes)
		printf("Il y a des enfants!");
else
	printf("Il n'y a personne!");

Ce problème se pose dans tous les langages qui offrent deux variétés d'instruction conditionnelle. On le résout soit par l'utilisation d'instructions vides :

 
Sélectionnez
if (nombrePersonnes != 0)
if (nombrePersonnes != nombreAdultes)
printf("Il y a des enfants!");
else
;
else
printf("Il n'y a personne!");

soit, plus simplement, en utilisant des blocs :

 
Sélectionnez
if (nombrePersonnes != 0) {
	if (nombrePersonnes != nombreAdultes)
		printf("Il y a des enfants!");
}
else
	printf("Il n'y a personne!");

Remarque. La syntaxe prévoit exactement une instruction entre la condition et le else. Par conséquent, un excès de points-virgules à la suite de instruction1 constitue une erreur. Voici une faute qu'on peut faire quand on débute :

 
Sélectionnez
if (nombrePersonnes != 0) {
	if (nombrePersonnes != nombreAdultes)
		printf("Il y a des enfants!");
};
else
	printf("Il n'y a personne!");

Il y a maintenant deux instructions entre la ligne du if et celle du else : une instruction-bloc f ... g et une instruction vide « ; ». Le compilateur signalera donc une erreur sur le else.

III-B-5. Instructions while et do...while

Ces instructions correspondent respectivement aux instructions while...do... et repeat...until... du langage Pascal. Notez que la syntaxe exige que la condition figure entre parenthèses. Formats :

 
Sélectionnez
while (expression)
	instruction

et

 
Sélectionnez
do
	instruction
while (expression);

Le fonctionnement de ces instructions est décrit par les organigrammes de la figure 4. Fondamentalement, il s'agit de réitérer l'exécution d'une certaine instruction tant qu'une certaine instruction, vue comme une condition, reste vraie. Dans la structure while on vérifie la condition avant d'exécuter l'instruction, tandis que dans la structure do...while on la vérifie après.

Fig. 4 - Instruction while (à gauche) et do...while (à droite)
Fig. 4 - Instruction while (à gauche) et do...while (à droite)

L'instruction do...while... exécute donc au moins une fois l'instruction qui constitue son corps avant d'évaluer la condition de continuation. C'est en cela qu'elle est l'analogue de l'instruction repeat...until du Pascal. Mais on notera que la condition figurant dans une instruction do...while (« faire tant que... ») et celle qui figurerait dans une instruction repeat...until équivalente (« répéter jusqu'à ce que... ») sont inverses l'une de l'autre.

III-B-6. Instruction for

Format :

 
Sélectionnez
for ( expr1 ; expr2 ; expr3 )
	instruction

Par définition, cette construction équivaut strictement à celle-ci :

 
Sélectionnez
expr1 ;
while (expr2) {
instruction
expr3;
}

Ainsi, dans la construction for(expr1 ; expr2 ; expr3) :

  • expr1 est l'expression qui effectue les initialisations nécessaires avant l'entrée dans la boucle ;
  • expr2 est le test de continuation de la boucle ; il est évalué avant l'exécution du corps de la boucle ;
  • expr3 est une expression (par exemple une incrémentation) évaluée à la fin du corps de la boucle.

Par exemple, l'instruction Pascal :

 
Sélectionnez
for i:=0 to 9 do
	t[i]:=0

se traduit tout naturellement en C

 
Sélectionnez
for (i = 0; i < 10; i++)
t[i] = 0;

Les expressions expr1 et expr3 peuvent être absentes (les points-virgules doivent cependant apparaitre). Par exemple

 
Sélectionnez
for ( ; expr2 ; )
	instruction
 
équivaut à
 
while (expr2)
	instruction

La condition de continuation expr2 peut elle aussi être absente. On considère alors qu'elle est toujours vraie. Ainsi, la boucle « indéfinie » peut se programmer :

 
Sélectionnez
for ( ; ; )
	instruction

Bien entendu, il faut dans ce cas que l'abandon de la boucle soit programmé à l'intérieur de son corps (sinon cette boucle indéfinie devient infinie !). Exemple :

 
Sélectionnez
for (;;) {
	printf("donne un nombre (0 pour sortir): ");
	scanf("%d", &n);
	if (n == 0)
break;
...
exploitation de la donnée n
...
}

Lorsque l'abandon de la boucle correspond aussi à l'abandon de la procédure courante, break est avantageusement remplacée par return comme dans :

 
Sélectionnez
char *adresse_de_fin(char *ch) {
	/* renvoie adresse du caractère qui suit la chaine ch */
for (;;)
	if (*ch++ == 0)
		return ch;
}

III-B-7. Instruction switch

Format :

 
Sélectionnez
switch ( expression )
	corps

Le corps de l'instruction switch prend la forme d'un bloc f...g renfermant une suite d'instructions entre lesquelles se trouvent des constructions de la forme

 
Sélectionnez
case expression-constante :

ou bien

 
Sélectionnez
default :

Le fonctionnement de cette instruction est le suivant : expression est évaluée :

  • s'il existe un énoncé case avec une constante qui égale la valeur de expression, le contrôle est transféré à l'instruction qui suit cet énoncé ;
  • si un tel case n'existe pas, et si l'énoncé default existe, alors le contrôle est transféré à l'instruction qui suit l'énoncé default ;
  • si la valeur de expression ne correspond à aucun case et s'il n'y a pas d'énoncé default, alors aucune instruction n'est exécutée.

Attention. Lorsqu'il y a branchement réussi à un énoncé case, toutes les instructions qui le suivent sont exécutées, jusqu'à la fin du bloc ou jusqu'à une instruction de rupture (break). Autrement dit, l'instruction switch s'apparente beaucoup plus à une sorte de goto « paramétré » (par la valeur de l'expression) qu'à l'instruction case...of... de Pascal.

Exemple (idiot) :

 
Sélectionnez
j = 0;
switch (i) {
	case 3:
		j++;
	case 2:
		j++;
	case 1:
		j++;
}

Si on suppose que i ne peut prendre que les valeurs 0 ... 3, alors l'instruction ci-dessus a le même effet que l'affectation j = i.

Pour obtenir un comportement similaire à celui du case...of... de Pascal, on doit utiliser l'instruction break, comme dans l'exemple suivant, dans lequel on compte la fréquence de chaque chiffre et des caractères blancs dans un texte :

 
Sélectionnez
nb_blancs = nb_autres = 0;
for (i = 0; i < 10; )
nb_chiffre[i++] = 0;
while ((c = getchar()) != EOF)
	switch (c) {
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			nb_chiffre[c - '0']++;
			break;
		case ' ':
		case '\n':
		case '\t':
			nb_blancs++;
			break;
			default:
			nb_autres++;
}

III-B-8. Instructions break et continue

Formats :

 
Sélectionnez
	break;
	continue;

Dans la portée d'une structure de contrôle (instructions for, while, do et switch), l'instruction break provoque l'abandon de la structure et le passage à l'instruction écrite immédiatement derrière. L'utilisation de break ailleurs qu'à l'intérieur d'une de ces quatre instructions constitue une erreur (que le compilateur signale).

Par exemple la construction

 
Sélectionnez
for (expr1 ; expr2 ; expr3) {
	...
	break;
	...
}

équivaut à

 
Sélectionnez
{
for (expr1 ; expr2 ; expr3) {
...
goto sortie;
...
}
sortie: ;
}

L'instruction continue est moins souvent utilisée. Dans la portée d'une structure de contrôle de type boucle (while, do ou for), elle produit l'abandon de l'itération courante et, le cas échéant, le démarrage de l'itération suivante. Elle agit comme si les instructions qui se trouvent entre l'instruction continue et la fin du corps de la boucle avaient été supprimées pour l'itération en cours : l'exécution continue par le test de la boucle (précédé, dans le cas de for, par l'exécution de l'expression d'incrémentation).

Lorsque plusieurs boucles sont imbriquées, c'est la plus profonde qui est concernée par les instructions break et continue. Dans ce cas, l'emploi de ces instructions ne nous semble pas œuvrer pour la clarté des programmes.

III-B-9. Instruction return

Formats :

 
Sélectionnez
return expression ;

et

 
Sélectionnez
return ;

Dans un cas comme dans l'autre l'instruction return provoque l'abandon de la fonction en cours et le retour à la fonction appelante.

Dans la première forme expression est évaluée ; son résultat est la valeur que la fonction renvoie à la fonction appelante ; c'est donc la valeur que l'appel de la fonction représente dans l'expression ou il figure. Si nécessaire, la valeur de expression est convertie dans le type de la fonction (déclaré dans l'en-tête), les conversions autorisées étant les mêmes que celles faites à l'occasion d'une affectation.

Dans la deuxième forme, la valeur retournée par la fonction reste indéterminée. On suppose dans ce cas que la fonction appelante n'utilise pas cette valeur ; il est donc prudent de déclarer void cette fonction. Absence d'instruction return dans une fonction. Lorsque la dernière instruction (dans l'ordre de l'exécution) d'une fonction est terminée, le contrôle est rendu également à la procédure appelante. Tout se passe comme si l'accolade fermante qui termine le corps de la fonction était en réalité écrite sous la forme

 
Sélectionnez
	...
	return;
}

précédentsommairesuivant

Copyright © 1988-2005 Henri Garreta. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.