Glossaire langage C
Dernière mise à jour : 2 janvier 2011
--> cat bloc.c
#include <stdio.h>
#include <stdlib.h>
int main( )
{ // début du bloc 1
int a = 1;
{ // début du bloc 1.1
printf("%d\n", a);
int a = 10;
printf("%d\n", a);
{ // début du bloc 1.1.1
printf("%d\n", a);
int a = 100;
printf("%d\n", a);
a ++;
printf("%d\n", a);
} // fin du bloc 1.1.1
a ++;
printf("%d\n", a);
} // fin du bloc 1.1
{ // début du bloc 1.2
printf("%d\n", a);
int a = 1000;
printf("%d\n", a);
a ++;
printf("%d\n", a);
} // fin du bloc 1.2
printf("%d\n", a);
return EXIT_SUCCESS; // return 0
} // fin du bloc 1
L'exécution de ce programme fournit les résultats suivants :
--> ./bloc
1
10
10
100
101
11
1
1000
1001
1
Ces opérateurs permettent de comparer des valeurs numériques ou des pointeurs :
const int a = 1;
int const b = 2;
De telles définitions de m et n entrainent une erreur lors de la compilation pour des tentatives de modification des valeurs de ces variables :
a ++; // error: increment of read-only variable 'a'
b = 2; // error: assignment of read-only variable 'b'
Si les deux écritures sont équivalentes dans le cas de variables de type classique (types de base, structures ou unions), les choses sont un peu différentes dans le cas de la définition de variables de type pointeur :
int a, b;
int * const ptr = &a;
Le fait que ptr soit constant, interdit dans la suite du programme toute instruction modifiant le pointeur :
ptr = &b; // erreur de compilation: assignment of read-only variable 'ptr'
int a, b;
int const * ptr = &a;
Le fait que l'espace pointé par ptr soit déclaré constant, interdit dans la suite du programme l'instruction
*ptr = 12; // erreur de compilation: assignment of read-only location
Il est intéressant d'observer ce qui se passe dans la séquence suivante : un simple avertissement (warning) signale que le pointeur référence une variable qualifiée const, ce que la définition du pointeur ne spécifie pas :
int const a;
int *ptr=&a; // warning:initialization discards qualifiers from pointer target type
Un exécutable est donc produit. L'exécution de l'instruction suivante, qui modifie indirectement la variable supposée constante n'a donné lieu à aucune erreur (mais un tel comportement non souhaitable n'est pas garanti sur tout système et risque de conduire sur certains à un arrêt brutal de l'exécution pour violation mémoire) :
*ptr = 1000;
printf("%d\n", a);
Cela signifie que:
L'opérateur -- est un opérateur unaire (un argument) applicable à un argument adressable (Lvalue).
C'est un opérateur à effet de bord (modifiant la valeur de l'espace mémoire correspondant à son argument) qui est utilisable sous deux formes :
do
{
suite_instructions
}
while (expression)
On parle d'effet de bord dans le cas d'un opérateur (par exemple =, ++ ou -=) modifiant l'un de ses opérandes ou d'une fonction qui modifie la valeur d'une variable globale ou d'une zone pointée par un de ses paramètres.
L'opérateur « , » permet de composer deux expressions.
Ainsi, une expression de la forme
exp1,exp2
est une expression dont l'évaluation entraîne successivement :
Ainsi l'évaluation de l'expression
x = (y = 4, z = 2 * y)
entraîne l'évaluation successive des expressions
exp1?exp2:exp3
dont l'évaluation est la suivante :
(a > 5) ? ((b <= 150) ? 3.25 : 9) : -13.0
a pour valeur 3.25.
La définition d'une fonction est constituée de
Une seule définition peut être attachée à un nom de fonction. Il n'y a pas la possibilité, comme en C++ ou Java, de donner plusieurs définitions de fonctions sous le même nom mais différant par leur signature (nombre ou type des paramètres).
L'évaluation et la transmission des paramètres depuis le module appelant vers le module appelé possèdent les propriétés importantes suivantes :
Cela signifie qu'il faut bannir les effets de bord dans les expressions utilisées dans les paramètres d'appel des fonctions.
Cela signifie que pour chacun des paramètres, une variable locale (portant le nom du paramètre formel) est créée sur la pile. Cette variable est initialisée avec la valeur du paramètre d'appel correspondant.
Le prototype d'une fonction doit normalement être décrit avant sa première utilisation (voir cependant la remarque).
Un tel prototype permet au compilateur de « vérifier » que l'utilisation de la fonction est conforme à sa définition.
Il est suivi d'un point virgule et non du corps de la fonction (contenant les instructions à exécuter), et ne nomme pas a priori les paramètres (s'il en contient nous parlerons de prototype étendu).
Ce petit exemple illustre ce que nous venons de dire :
--> cat prototype.c
#include <stdio.h>
#include <stdlib.h>
void g(void){
printf("Bonjour\n");
}
int main( ) {
float x = f(3, 5);
g(3);
printf("%f\n", x);
return EXIT_SUCCESS;
}
int f(int x, int y, int z){
return x + y + z;
}
--> gcc prototype.c
prototype.c: In function 'main':
prototype.c:6: warning: implicit declaration of function 'f'
prototype.c:7: error: too many arguments to function 'g'
-->
/* deux paramètres de type int et retour d'un int */
int somme(int, int);
/* un paramètre de type size_t et retour d'un pointeur sur char */
void *malloc (size_t);
for(exp_initialisation; exp_condition; exp_modification)
instruction
dans laquelle :
? Une telle boucle est équivalente à la boucle while suivante :
exp_initialisation;
while(exp_condition)
{
instruction
exp_modification;
}
Il s'agit d'une structure de contrôle permettant de réaliser des tests et d'exécuter des instructions ou blocs différents selon la valeur de ces tests.
Dans ce qui suit instruction désigne soit une instruction simple, soit un bloc (délimité par des accolades) et expression désigne une expression quelconque dont on considérera la valeur logique déduite de sa valeur..
if (expression)
instruction
L'instruction n'est exécutée que si la valeur logique de l'expression est vraie
if (expression)
instruction1
else
instruction2
L'expression est évaluée : si sa valeur logique est vraie, l'instruction1 est exécutée et sinon c'est l'instruction2 qui est exécutée.
Dans la séquence suivante, les if et else de même couleur sont associés :
if(a)
if(b)
x = 3;
else if(c)
x = 4;
else {
if(d)
x = 5;
}
else
x = 6;
L'opérateur ++ est un opérateur unaire (un argument) applicable à un argument adressable (Lvalue).
C'est un opérateur à effet de bord (modifiant la valeur de l'espace mémoire correspondant à son argument) qui est utilisable sous deux formes :
Toute instruction élémentaire se déduit d'une expression en lui ajoutant le caractère « ; »
Le caractère « ; » fait donc partie de l'instruction et en marque la terminaison.
Ce n'est pas, comme c'est le cas dans d'autres langages (Pascal par exemple), un opérateur de composition ou un séparateur d'instructions.
Remarque : une instruction telle que « i + 1; » est correcte mais ne fait rien puisqu'elle ne modifie rien en mémoire (pas d'effet de bord).
L'instruction « ; » réduite à un point virgule est l'instruction vide. Elle est utilisable par exemple comme corps de boucle ou dans une instruction conditionnelle en partie if ou else.
La fonction main constitue le « point d'entrée » dans un binaire exécutable, c'est-à-dire qu'au lancement du programme, c'est cette fonction qui est automatiquement appelée.
Ainsi :
Arguments/paramètres de la fonction main : la fonction est normalement appelée sous deux formes (c'est ce qu'attend par exemple le compilatuer gcc) :
Consultation d'un programme parcourant les paramètres.
Ils permettent de composer de composer les valeurs logiques d'expression et par là de réaliser des tests généraux.
Quatre opérateurs sont disponibles :
Il existe 15 niveaux de priorité.
Pour chaque niveau, l'associativité est réalisée de gauche à droite ou de droite à gauche comme indiqué.
A titre d'exemple, l'associativité des opérateurs arithmétiques additifs est réalisée de gauche à droite et ces opérateurs sont moins prioritaires que les opérateurs arithmétiques mutiplicatifs.
Cela signifie que l'expression
3-4+5*6-7-8+1
est évaluée comme
(((((3-4)+(5*6))-7)-8)+1)
et a donc 16 comme valeur.
L'associativité des opérateurs d'affectation étant de droite à gauche et l'addition étant prioritaire par rapport aux affectations, l'expression
a=b+=b*=c=2+b
est évaluée comme
a=(b+=(b*=(c=(2+b))))
Si b vaut initialement 3, après évaluation de cette expression, les nouvelles valeurs des variables a, b et c sont respectivement (par effet de bord) 30, 30 et 5.
Opérateur |
Notation |
Exemples |
Associativité |
indexation |
[ ] |
t[i][j] |
de gauche à droite |
accès aux champs |
. -> |
c.champ pointeur->champ |
de gauche à droite |
appel de fonction |
( ) |
f(a, b, 3) |
de gauche à droite |
Opérateur |
Notation |
Exemples |
Associativité |
négation logique |
! |
!(x == 4) !x |
de droite à gauche |
négation bit à bit |
˜ |
˜ent !x |
de droite à gauche |
incrémentation |
++ |
++x x++ |
de droite à gauche |
décrémentation |
-- |
--x x-- |
de droite à gauche |
moins unaire |
- |
-(x + 1) |
de droite à gauche |
coercition (cast) |
(type) |
(char *) (float) |
de droite à gauche |
taille |
sizeof |
sizeof(int) sizeof(n) |
de droite à gauche |
référencement (adresse de) |
& |
&n |
de droite à gauche |
déréférencement (valeur pointée) |
* |
*ptr |
de droite à gauche |
Opérateur |
Notation |
Exemples |
Associativité |
mutiplication |
* |
5 * x |
de gauche à droite |
division |
/ |
x / y |
de gauche à droite |
modulo |
% |
x % 5 |
de gauche à droite |
Opérateur |
Notation |
Exemples |
Associativité |
addition |
+ |
x + 8 |
de gauche à droite |
soustraction |
- |
a - b |
de gauche à droite |
Opérateur |
Notation |
Exemples |
Associativité |
décalage à gauche |
<< |
a << 4 |
de gauche à droite |
décalage à droite |
>> |
x >> n |
de gauche à droite |
Opérateur |
Notation |
Exemples |
Associativité |
plus grand |
> |
a > b |
de gauche à droite |
plus grand ou égal |
>= |
a >= 5 |
de gauche à droite |
plus petit |
< |
y < a |
de gauche à droite |
plus petit ou égal |
<= |
a <= b |
de gauche à droite |
Opérateur |
Notation |
Exemples |
Associativité |
égalité |
== |
(x + 1) == (y + 2) |
de gauche à droite |
négalité |
!= |
z != 5 |
de gauche à droite |
Opérateur |
Notation |
Exemples |
Associativité |
ET bit à bit |
& |
x & 0x3456AF01 |
de gauche à droite |
Opérateur |
Notation |
Exemples |
Associativité |
XOR bit à bit |
^ |
x ^ y |
de gauche à droite |
Opérateur |
Notation |
Exemples |
Associativité |
OU bit à bit |
| |
x | 0234 |
de gauche à droite |
Opérateur |
Notation |
Exemples |
Associativité |
conjonction logique (ET) |
&& |
a > b) && (z < 5) |
de gauche à droite |
Opérateur |
Notation |
Exemples |
Associativité |
disjonction logique (OU) |
|| |
(a != 0) || (b / a > 5) |
de gauche à droite |
Opérateur |
Notation |
Exemples |
Associativité |
expression conditionnelle |
? : |
(x>0)?x:-x |
de droite à gauche |
Opérateur |
Notation |
Exemples |
Associativité |
affectation simple |
= |
a = 5 |
de droite à gauche |
affectations étendues (arithmétiques) |
+= -= *= /= %= |
a+=5 a*=(b+1) |
de droite à gauche |
affectations étendues (opérations bit à bit) |
&= ^= |= |
a&=b a^=0x789A |
de droite à gauche |
affectations étendues (décalages) |
<<= >>= |
a<<=5 |
de droite à gauche |
Opérateur |
Notation |
Exemples |
Associativité |
composition d'instructions |
, |
a=2,b=a+3,z+=5 |
de gauche à droite |
Souvent appelée aiguillage, cette structure permet, après évaluation d'une expression, de « se brancher » (et donc d'y poursuivre l'exécution) en un point (une étiquette) correspondant à la valeur de l'expression.
La syntaxe de la structure switch est la suivante (chaque suite_instructions peut éventuellement être vide et consiste sinon en la concaténation d'instructions, dont l'instruction « break; ») :
switch ( expression_entière )
{
case constante1 : suite_instructions1
case constante2 : suite_instructions2
........
case constanteN : suite_instructionsN
default : suite_instructions
}
// point de reprise d'exécution en cas de break dans le switch
La dernière alternative, notée default correspond au cas par défaut (si la valeur de l'expression ne correspond à aucun des cas prévus).
Elle est facultative. Cependant, pour des raisons de lisibilité des programmes, son usage est conseillé, quitte à lui associer une liste d'instructions vide;
Les étiquettes de chacun des cas sont des expressions constantes (que le compilateur doit donc pouvoir calculer).
la sémantique de cette structure de contrôle est la suivante :
un exemple utilisant un switch pour imprimer un message différent selon la nature du caractère contrôlant l'instruction :
switch(c) {
case 'A': case 'E': case 'I': case 'O': case 'U': case 'Y':
printf("Le caractere lu est une voyelle\n");
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
printf("Le caractere lu est un chiffre\n");
break;
case 'B': case 'C': case 'D': case 'F': case 'G':
case 'H': case 'J': case 'K': case 'L': case 'M':
case 'N': case 'P': case 'Q': case 'R': case 'S':
case 'T': case 'V': case 'W': case 'X': case 'Z':
printf("Le caractere lu est une consonne\n");
break;
default :
printf("Le caractere lu n'est ni une lettre, ni un chiffre\n");
// break;
}
Il a été introduit avec la norme ANSI du langage pour :
Le type void est un type incomplet (de taille non définie) : il correspondant à une absence de type et par suite :
--> cat essai.c
int main(){void a; }
--> gcc a.c
essai.c: Dans la fonction « main »:
essai.c:2: error: variable or field `a' declared void
-->
La forme générale de la boucle est :
while (expression)
instruction
où instruction est une instruction simple ou un bloc.
Il s'agit d'une structure de contrôle permettant d'itérer une instruction (élémentaire ou bloc) tant qu'une certaine condition est vraie.
L'expression contrôlant la boucle est évaluée à l'entrée de la boucle et donc l'instruction n'est éventuellement pas exécutée.
Copyright © <2011>, <Lycée de Taaone/Escot>
Created with the Freeware Edition of HelpNDoc: Easily create CHM Help documents