Next: 2 Opérateurs et expressions
Up: Le langage C
Previous: Le langage C
00011010 0001 0010
demande à la machine d'effectuer l'opération 1+2. A chaque type de machine correspond, un jeu d'instructions spécifique; de même le codage des instructions est également dépendante de la machine utilisée.
Même si ce langage est le seul qui soit compris par l'ordinateur, il n'est pas le seul moyen de communiquer avec celui-ci. En effet, on a très tôt éprouvé le besoin d'humaniser cette communication et la première tentative en ce sens est l'invention du langage assembleur. Par exemple, l'instruction assembleur
demande à la machine d'effectuer l'opération 1+2. Ce langage est très proche du langage machine et se contente de donner des noms mnémotechniques pour les instructions ainsi qu'une manière plus naturelle de désigner des entiers. Le langage assembleur fut suivi par des langages plus sophistiqués. En particulier, on distingue les langages qui permettent
add $1 $2
Ces langages ont tous pour ambition de faciliter la programmation en la rendant plus proche du << langage humain >>. Tous ces langages de programmation ont besoin d'un traducteur pour être compris de la machine. De tels traducteurs sont généralement appelés interpréteurs ou compilateurs. En ce qui nous concerne, nous nous intéresserons qu'aux compilateurs: ils traduisent le texte écrit par un programmeur en un programme exécutable (compréhensible par la machine).
Un programme C est un texte écrit avec un éditeur de texte, respectant une certaine syntaxe et stocké sous forme d'un ou plusieurs fichiers (généralement avec l'extension .c). A l'opposé du langage assembleur, les instructions du langage C sont obligatoirement encapsulées dans des fonctions et il existe une fonction privilégiée appelée main qui est le point de départ de tout programme. Voici, un exemple de programme C
qui se contente d'afficher la chaîne de caractères Bonjour à l'écran. Pour afficher cette chaîne, le programme fait appel à la fonction printf qui fait partie de l'une des fonctions prédéfinies fournies avec tout compilateur C. L'ensemble de ces fonctions prédéfinies (appelé bibliothèque C) est stocké dans un ou plusieurs fichier(s). La traduction du fichier texte ci-dessus en un programme exécutable se décompose en deux phases:
int main() { printf("Bonjour"); return 1; }
Outre l'assemblage des divers fichiers objets, l'édition des liens inclut les définitions des fonctions prédéfinies utilisées par le programme.
Chaque fichier texte est appelé module et est composé (comme nous le verrons plus loin)
Par exemple, pour utiliser la fonction printf, il faut inclure le fichier stdio.h, stdio.h qui contient les déclaration de variables externes et les protoytpes de fonctions de la bibliothèque d'entrée-sortie standard (standard input output),
#include nom de fichier
Voici la version correcte du programme présenté précédemment (section 1.1 page
#include
#includeint main() { printf("Bonjour"); return 1; }
Un commentaire commence par les caractères /* et se terminent par */. A l'intérieur de ces délimiteurs toute suite de caractères est valide (sauf évidemment */).
/* Ce programme imprime la chaine de caractéres "bonjour" à l'écran */#include
/* Fichier include pour pouvoir utiliser la fonction printf */ int main() { printf("Bonjour"); return 1; }
Rappelons qu'un nombre n en notation décimale est représenté en base b par le nombre ai ai-1 ... a1 a0
où
n = am * bm + am-1 * bm-1 + ... + a1 * b + a0 avec 0 < ai < b.
Voici une liste des bases habituellement utilisées en informatique
Base | Notation | Symboles |
2 | binaire | 0, 1 |
8 | octale | 0, 1, ... 7 |
10 | décimale | 0, 1, ... 9 |
16 | hexadécimale | 0, 1, ... 9, a, b, c, d, e, f |
On appelle bit de signe, le bit le plus élevé (bit de poids fort) de le représentation d'un entier.
En C, on dispose de divers types d'entiers qui se distinguent par la place qu'ils occupent en mémoire :
Décimal | Caractère |
NULL | |
... | ... |
48 | |
... | ... |
57 | 9 |
... | ... |
65 | A |
... | ... |
90 | Z |
... | ... |
97 | a |
... | ... |
122 | z |
... | ... |
127 |
Comme vous le constatez, le type char n'est qu'un entier codé sur un octet. Il en découle que toutes les opérations autorisées sur les entiers peuvent être utilisées sur les caractères. Aussi surprenant que cela puisse paraître, on peut ajouter ou soustraire deux caractères, ajouter ou soustraire un entier à un caractère.
Une utilisation classique de cette souplesse d'utilisation est la conversion d'un caractère c désigant un chiffre en sa valeur v correspondante:v = c - '0'
char
sont des caractères valides. Les programmes qui manipulent les caractères
doivent disposer d'un caractère (ou pseudo caractère) supplémentaire,
distinct de tout caractère valide. Cette valeur particulière sera utilisée
pour vérifier qu'on l'on atteint la fin des données.
Cette valeur a pour nom
EOF
et est définie dans le fichier <stdio.h>
.
Le type short représente un entier signé codé sur 2 octets (de -32768 à 32767) et le type unsigned short représente un entier non signé codé sur 2 octets (de 0 à 65535). Le type long (ou int pour nos machines) représente un entier signé codé sur 4 octets (de -2147843648 à 2147843647) et le type unsigned long (ou unsigned int pour nos machines) représente un entier non signé codé sur 4 octets (de 0 à 4294967295).
Les nombres à virgule flottante (abusivement appelés réels) servent à coder de manière approchée les nombres réels. Un nombre à virgule flottante est composée d'un signe, d'une mantisse et d'un exposant. On dispose de trois types de nombres à virgule flottante, les types float, double et long double.
Un float est codé sur 4 octets avec 1 bit de signe, 23 bits de mantisse et 8 bits d'exposant (valeurs comprises entre 3.4 * 10-38 et 3.4 * 1038 ).
Un double est codé sur 8 octets avec 1 bit de signe, 52 bits de mantisse et 11 bits d'exposant (valeurs comprises entre 1.7 * 10-308 et 1.7 * 10308 ).
Le type d'une constante entière est le << plus petit >> type dans lequel il peut être représenté :
- notation décimale : int, sinon long, sinon unsigned long
- notation octale ou décimale : int, sinon unsigned int, sinon unsigned long
Des suffixes permettent de changer cette classification :
- U, u : constante de type unsigned
- L, l : constante de type long
Une constante flottante se présente sous la forme d'une suite de chiffres (partie entière), un point qui joue le rôle de virgule, une suite de chiffres (partie fractionnaire), une des deux lettres e ou E, éventuellement le signe + ou - suivi d'une suite de chiffres (valeur absolue de l'exposant)
La partie entière ou la partie fractionnaire peut être omise (pas les deux); de même le point ou l'exposant peut être omis (pas les deux).
Une constante flottante est supposée être de type double. Le suffixe F indique qu'elle est de type float. Le suffixe LF indique qu'elle est de type long double.
Les constantes de type caractère se note entre apostrophes:
'
se note '\''
et le caractère
\
se note '\\'
. On peut également représenter des
caractères non imprimables à l'aide de séquences d'échappement.
Voici une liste non exhaustive de caractères non imprimable:
Séquence | |
\n |
nouvelle ligne |
\t |
tabulation horizontale |
\v |
tabulation verticale |
\b |
retour d'un caractère en arrière |
\r |
retour chariot |
\f |
saut de page |
\a |
beep |
\' |
apostrophe |
\" |
guillemet |
\\ |
anti-slash |
\ddd |
code ASCII en notation octale |
\xddd |
code ASCII en notation hexadécimale |
Une chaîne de caractères est une suite de caractères (éventuellement vide) entre guillemets. Il en découle que l'on est autorisé à utiliser les séquences d'échappement dans les chaînes. La fonction suivante
produit la sortie suivante :printf("Bonjour\n\tComment ca va \n");
En mémoire, une chaîne de caractères est une suite de caractères consécutifs et dont le dernier élément est le caractère nul
Bonjour Comment ca va
'\0'
.
Une chaîne de caractère doit être écrite sur une seule ligne.
Lorsqu'il est trop long pour tenir
une même ligne, on decoupe celle-ci en plusieurs bouts; chaque bout étant écrite sur une seule ligne et on masque le retour à la ligne par le caractère \
.
Les deux instructions suivantes sont équivalentes.
x = "abcdefghijklm\ nopqrstuvwxyz\ ABCDEFGHIJKLM\ NOPQRSTUVWXYZ"; x = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
Dans un contexte qui exige une valeur booléenne (comme les tests, par exemple), un entier non nul équivaut à vrai et la valeur zero équivaut à faux: les expressions (expr) et (expr != 0) sont équivalentes. De même, une fonction qui retourne une valeur boolénne pourra retourner une valeur non nulle comme équivalent à vrai et la valeur 0 comme équivalent à faux.
Une variable possède
_
.
L'emplacement d'une variable en mémoire est désigné par le terme adresse de la variable. Cette adresse est définie une fois pour toutes et est évidemment invariante tout au long du programme.
Contrairement aux constantes, le contenu d'une variable peut être modifée à volonté; ce qui ne change pas c'est l'adresse de la variable.
La notion de variable en informatique n'a strictement rien de commum avec celle de varibale en mathématique. En infomatique, il ne s'agit de rien de plus que d'une adresse en mémoire.
Initialiser une variable consiste à remplir, avec une constante, la zone
mémoire réservée à cette variable. Cette opération s'effectue avec l'opérateur
=Il ne faut pas confondre l'initialisation et affectation.
Malheuresement, ces deux opérations utilisent le même symbole =.
Il est parfois utile de déclarer une variable sans la définir. Une déclarartion de variable définit un nom et un type mais n'alloue par la place mémoire. En effet, lorsque le programme que l'on réalise est décomposé en plusieurs modules, une même variable, utilisée dans plusieurs modules, doit être déclarée dans chacun de ces modules. Par contre, on ne définira cette variable que dans un seul de ces modules
int x = 2; char c = 'c'; float f = 1.3;
L'intialisation d'une variable ne peut se faire que lors de sa définition. Par contre, il ne faut pas initialiser une variable lors de sa déclaration. Dans ce qui suit, lorsqu'il n'y aura pas d'ambiguïté, on confondra les deux termes : déclaration et définition.
La syntaxe de la déclaration d'une variable est de la forme suivante:
= expression |
|
|||
---|---|---|---|---|
= rien |
Nous verrons dans le chapitre refcdd le sens des mots clés auto, extern, static, register, volatile, etc.
Un certain nombre d'identificateurs sont reservés et ne peuvent être utilisés comme noms de variables. Voici la liste des noms réservés
int x, y = 0, z; extern float a, b; static unsigned short cpt = 1000;
auto | break | case | char | const | continue | default | do |
double | else | enum | extern | float | for | goto | if |
int | long | register | return | short | signed | sizeof | static |
struct | switch | typedef | union | unsigned | void | volatile | while |
Pour plus de précision, se reporter à la page .
%
suivi d'une ou plusieurs lettres clé. Voici à titre d'exemple, quelques spécifiactions possibles;
on trouvera, dans le chapitre 8, une présentation détaillée de ces spécifications.
%c |
caractère |
%s |
chaîne |
%d |
entier |
%u |
entier non signé |
%f ,%g |
flottant |
%e ,%E |
flottant avec exposant |
... |
printf("Coucou")
: pas d'arguments, format est constituée
uniquement de la chaîne "Coucou"
.
printf("%s", "Coucou")
: dans cet appel, il y a un argument de type chaîne
(%s
) qui est la chaîne "Coucou"
.
printf("%d", 3)
: dans cet appel, il y a un argument de type entier et qui vaut 3.
printf("la somme de %d et de %d vaut %d", 3, 5, 3+5)
: dans cet appel, il y a
trois arguments de type entier.
Il n'y a aucun test de vérification de la cohérence entre le format et le
type des arguments fournis.
L'appel printf("%s", 333)
est parfaitement admis par le compilateur;
aucune erreur n'est signalée mais l'exécution de cette
instruction produira un résultat curieux.
Il convient donc au programmeur de s'assurer de la cohérence
entre le format et les arguments founis.
marginparexemple
&
devant chaque argument à imprimer. Nous verrons dans le
chapitre 4 le sens de ce symbole.
Lorsqu'une lecture à la console est faite avec scanf, il est impératif de << rentrer >> quelque chose qui est rigoureusement identique au format que l'on a défini. Nous verrons dans le chapiter 8 les problèmes liés au non respect du formatage.
scanf("%d\n", &x)
: le contenu de la variable x est rempli avec ce que
l'utilisateur founira à la console. Il faut donc << entrer >> un entier suivi d'un
retour à la ligne.
Ici non plus, il n'y a aucune vérification (ni à la compilation, ni à l'exécution) de la cohérence entre le format et le type des arguments founis.
int getchar(void) et char *gets(char *s)
qui retourne (voir chapitre 4) un caractère (resp. une chaîne de caractères) lu à la console.
Touraivane