Microsoft Word - Poly-C - Ii20
Microsoft Word - Poly-C - Ii20
Microsoft Word - Poly-C - Ii20
Otmane
-Lnom-de-répertoire : spécifie le répertoire dans lequel doivent être recherchées les librairies précompilées
(en plus du répertoire usuel).
1 Un peu d’histoire -o nom-de-fichier : spécifie le nom du fichier produit. Par défaut, le exécutable fichier s'appelle a.out.
-O, -O1, -O2, -O3 : options d'optimisations. Sans ces options, le but du compilateur est de minimiser le coût
Le C a été conçu en 1972 par Dennis Richie et Ken Thompson, chercheurs aux Bell Labs, afin de développer un de la compilation. En rajoutant l'une de ces options, le compilateur tente de réduire la taille du code
système d'exploitation UNIX sur un DEC PDP-11. En 1978, Brian Kernighan et Dennis Richie publient la exécutable et le temps d'exécution. Les options correspondent à différents niveaux d'optimisation : -O1
définition classique du C dans le livre The C Programming language [6]. Le C devenant de plus en plus (similaire à -O) correspond à une faible optimisation, -O3 à l'optimisation maximale.
populaire dans les années 80, plusieurs groupes mirent sur le marché des compilateurs comportant des extensions -S : n'active que le préprocesseur et le compilateur ; produit un fichier assembleur.
particulières. En 1983, l'ANSI (American National Standards Institute) décida de normaliser le langage ; ce -v : imprime la liste des commandes exécutées par les différentes étapes de la compilation.
travail s'acheva en 1989 par la définition de la norme ANSI C. Celle-ci fut reprise telle quelle par l'ISO -W : imprime des messages d'avertissement (warning) supplémentaires.
(International Standards Organization) en 1990. C'est ce standard, ANSI C, qui est décrit dans le présent -Wall : imprime tous les messages d'avertissement.
document.
4 5
Module II20 (Remise à niveau en programmation C) S.Otmane Module II20 (Remise à niveau en programmation C) S.Otmane
auto const double float int short struct unsigned Une instruction composée d'un spécificateur de type et d'une liste d'identificateurs séparés par une virgule est une
break continue else for long signed switch void déclaration. Par exemple,
case default enum goto register sizeof typedef volatile
char do extern if return static union while int a;
int b = 2, c;
que l'on peut ranger en catégories : double x = 2.38e4;
char message[80];
• les spécificateurs de stockage
ATTENTION : En C, toute variable doit faire l'objet d'une déclaration avant d'être utilisée.
auto register static extern typedef
3.3 Les commentaires Les fonctions secondaires peuvent être placées indifféremment avant ou après la fonction principale. Une
fonction secondaire peut se décrire de la manière suivante :
Un commentaire débute par /* et se termine par */. Par exemple,
type ma_fonction ( arguments )
/* Ceci est un commentaire */ {déclarations de variables internes
instructions
On ne peut pas imbriquer des commentaires. Quand on met en commentaire un morceau de programme, il faut }
donc veiller à ce que celui-ci ne contienne pas de commentaire.
Cette fonction retournera un objet dont le type sera type (à l'aide d'une instruction comme return objet;). Les
arguments de la fonction obéissent à une syntaxe voisine de celle des déclarations : on met en argument de la
fonction une suite d'expressions type objet séparées par des virgules. Par exemple, la fonction secondaire
4 Structure d'un programme C suivante calcule le produit de deux entiers :
Une expression est une suite de composants élémentaires syntaxiquement correcte, par exemple int produit(int a, int b)
x=0 {
ou bien int resultat;
Une instruction est une expression suivie d'un point-virgule. Le point-virgule signifie en quelque sorte ``évaluer
cette expression''. Plusieurs instructions peuvent être rassemblées par des accolades { et } pour former une
instruction composée ou bloc qui est syntaxiquement équivalent à une instruction. Par exemple,
6 7
Module II20 (Remise à niveau en programmation C) S.Otmane Module II20 (Remise à niveau en programmation C) S.Otmane
Le C est un langage typé. Cela signifie en particulier que toute variable, constante ou fonction est d'un type Une constante est une valeur qui apparaît littéralement dans le code source d'un programme, le type de la
précis. Le type d'un objet définit la façon dont il est représenté en mémoire. constante étant déterminé par la façon dont la constante est écrite. Les constantes peuvent être de 4 types : entier,
flottant (nombre réel), caractère, énumération. Ces constantes vont être utilisées, par exemple, pour initialiser
La mémoire de l'ordinateur se décompose en une suite continue d'octets. Chaque octet de la mémoire est une variable.
caractérisé par son adresse, qui est un entier. Deux octets contigus en mémoire ont des adresses qui diffèrent
d'une unité. Quand une variable est définie, il lui est attribué une adresse. Cette variable correspondra à une zone
mémoire dont la longueur (le nombre d'octets) est fixée par le type. 6.1 Les constantes entières
La taille mémoire correspondant aux différents types dépend des compilateurs ; toutefois, la norme ANSI Une constante entière peut être représentée de 3 manières différentes suivant la base dans laquelle elle est écrite :
spécifie un certain nombre de contraintes.
décimale : par exemple, 0 et 2437348 sont des constantes entières décimales.
Les types de base en C concernent les caractères, les entiers et les flottants (nombres réels). Ils sont désignés par octale : la représentation octale d'un entier correspond à sa décomposition en base 8. Les constantes octales
les mots-clefs suivants : doivent commencer par un zéro. Par exemple, les représentations octales des entiers 0 et 255 sont
respectivement 00 et 0377.
char int float double short long unsigned hexadécimale : la représentation hexadécimale d'un entier correspond à sa décomposition en base 16. Les lettres
de a à f sont utilisées pour représenter les nombres de 10 à 15. Les constantes hexadécimales doivent
commencer par 0x ou 0X. Par exemple, les représentations hexadécimales de 14 et 255 sont respectivement
5.1 Le type caractère 0xe et 0xff.
Le mot-clef char désigne un objet de type caractère. Un char peut contenir n'importe quel élément du jeu de On peut spécifier explicitement le format d'une constante entière en la suffixant par u ou U pour indiquer qu'elle
caractères de la machine utilisée. La plupart du temps, un objet de type char est codé sur un octet ; c'est l'objet le est non signée, ou en la suffixant par l ou L pour indiquer qu'elle est de type long. Par exemple :
plus élémentaire en C. Le jeu de caractères utilisé correspond généralement au codage ASCII (sur 7 bits). La
plupart des machines utilisent désormais le jeu de caractères ISO-8859 (sur 8 bits), dont les 128 premiers
caractères correspondent aux caractères ASCII. Les 128 derniers caractères (codés sur 8 bits) sont utilisés pour Constante type
les caractères propres aux différentes langues. 1234 int
02322 int /* octal */
Une des particularités du type char en C est qu'il peut être assimilé à un entier : tout objet de type char peut être 0x4D2 int /* hexadécimal */
utilisé dans une expression qui utilise des objets de type entier. Par exemple, si c est de type char, l'expression c 123456789L long
+ 1 est valide. Elle désigne le caractère suivant dans le code ASCII. Ainsi, le programme suivant imprime le 1234U unsigned int
caractère 'B'. 123456789UL unsigned long int
main()
{ 6.2 Les constantes réelles
char c = 'A';
printf("%c", c + 1); Les constantes réelles sont représentées par la notation classique par mantisse et exposant. L'exposant est
} introduit par la lettre e ou E ; il s'agit d'un nombre décimal éventuellement signé.
Par défaut, une constante réelle est représentée avec le format du type double. On peut cependant influer sur la
représentation interne de la constante en lui ajoutant un des suffixes f (indifféremment F) ou l (indifféremment
5.2 Les types entiers L). Les suffixes f et F forcent la représentation de la constante sous forme d'un float, les suffixes l et L forcent la
représentation sous forme d'un long double. Par exemple :
Le mot-clef désignant le type entier est int. Un objet de type int est représenté par un mot ``naturel'' de la
machine utilisée, 32 bits pour un DEC alpha ou un PC Intel.
Constante type
Le type int peut être précédé d'un attribut de précision (short ou long) et/ou d'un attribut de représentation 12.34 double
(unsigned).Un objet de type short int a au moins la taille d'un char et au plus la taille d'un int. En général, un 12.3e-4 double
short int est codé sur 16 bits. Un objet de type long int a au moins la taille d'un int (64 bits sur un DEC alpha, 32 12.34F float
bits sur un PC Intel). 12.34L long double
8 9
Module II20 (Remise à niveau en programmation C) S.Otmane Module II20 (Remise à niveau en programmation C) S.Otmane
en octal du caractère. On peut aussi écrire '\xcode-hexa' où code-hexa est le code en hexadécimal du caractère
(cf. page X). Par exemple, '\33' et '\x1b' désignent le caractère escape. Toutefois, les caractères non-imprimables 7.2 Les opérateurs arithmétiques
les plus fréquents disposent aussi d'une notation plus simple :
Les opérateurs arithmétiques classiques sont l'opérateur unaire - (changement de signe) ainsi que les opérateurs
\n nouvelle ligne binaires
\r retour chariot
\t tabulation horizontale + addition
\f saut de page - soustraction
\v tabulation verticale * multiplication
\a signal d'alerte / division
\b retour arrière % reste de la division (modulo)
Ces opérateurs agissent de la façon attendue sur les entiers comme sur les flottants. Leurs seules spécificités sont
les suivantes :
6.4 Les constantes chaînes de caractères
Contrairement à d'autres langages, le C ne dispose que de la notation / pour désigner à la fois la division entière
Une chaîne de caractères est une suite de caractères entourés par des guillemets. Par exemple, et la division entre flottants. Si les deux opérandes sont de type entier, l'opérateur / produira une division entière
(quotient de la division). Par contre, il délivrera une valeur flottante dès que l'un des opérandes est un flottant.
"Ceci est une chaîne de caractères" Par exemple,
Une chaîne de caractères peut contenir des caractères non imprimables, désignés par les représentations vues float x;
précédemment. Par exemple, x = 3 / 2;
A l'intérieur d'une chaîne de caractères, le caractère " doit être désigné par \". Enfin, le caractère \ suivi d'un x = 3 / 2.;
passage à la ligne est ignoré. Cela permet de faire tenir de longues chaînes de caractères sur plusieurs lignes. Par
exemple, affecte à x la valeur 1.5.
"ceci est une longue longue longue longue longue longue longue longue \ L'opérateur % ne s'applique qu'à des opérandes de type entier. Si l'un des deux opérandes est négatif, le signe du
chaîne de caractères" reste dépend de l'implémentation, mais il est en général le même que celui du dividende.
Notons enfin qu'il n'y a pas en C d'opérateur effectuant l'élévation à la puissance. De façon générale, il faut
utiliser la fonction pow(x,y) de la librairie math.h
7 Les opérateurs pour calculer x^y.
7.1 L'affectation
En C, l'affectation est un opérateur à part entière. Elle est symbolisée par le signe =. Sa syntaxe est la suivante : 7.3 Les opérateurs relationnels
variable = expression
Le terme de gauche de l'affectation peut être une variable simple, un élément de tableau mais pas une constante. > supérieur
Cette expression a pour effet d'évaluer expression et d'affecter la valeur obtenue à variable. De plus, cette >= supérieur ou égal
expression possède une valeur, qui est celle expression. Ainsi, l'expression i = 5 vaut 5. < strictement inférieur
<= inférieur ou égal
L'affectation effectue une conversion de type implicite : la valeur de l'expression (terme de droite) est convertie == égal
dans le type du terme de gauche.Par exemple, le programme suivant != différent
10 11
Module II20 (Remise à niveau en programmation C) S.Otmane Module II20 (Remise à niveau en programmation C) S.Otmane
if (a = b)
printf("\n a et b sont egaux \n"); 7.7 Les opérateurs d'incrémentation et de décrémentation
else
printf("\n a et b sont differents \n"); Les opérateurs d'incrémentation ++ et de décrémentation -- s'utilisent aussi bien en suffixe (i++) qu'en préfixe
} (++i). Dans les deux cas la variable i sera incrémentée, toutefois dans la notation suffixe la valeur retournée sera
l'ancienne valeur de i alors que dans la notation préfixe se sera la nouvelle. Par exemple,
imprime à l'écran a et b sont egaux !
int a = 3, b, c;
b = ++a; /* a et b valent 4 */
7.4 Les opérateurs logiques booléens c = b++; /* c vaut 4 et b vaut 5 */
Comme pour les opérateurs de comparaison, la valeur retournée par ces opérateurs est un int qui vaut 1 si la Cette expression est alors évaluée de gauche à droite. Sa valeur sera la valeur de l'expression de droite. Par
condition est vraie et 0 sinon. exemple, le programme
12 13
Module II20 (Remise à niveau en programmation C) S.Otmane Module II20 (Remise à niveau en programmation C) S.Otmane
Si la valeur de expression est égale à l'une des constantes, la liste d'instructions correspondant est exécutée.
Sinon la liste d'instructions
M correspondant à default est exécutée. L'instruction default est facultative.
7.11 L'opérateur adresse
L'opérateur d'adresse & appliqué à une variable retourne l'adresse mémoire de cette variable. La syntaxe est 9 Les boucles
&objet Les boucles permettent de répéter une série d'instructions tant qu'une certaine condition n'est pas vérifiée.
Tant que expression est vérifiée (i.e., non nulle), instruction est exécutée. Si expression est nulle au départ,
8.1 Branchement conditionnel « if---else » instruction ne sera jamais exécutée.
instruction peut évidemment être une instruction composée. Par exemple, le programme suivant imprime les
La forme la plus générale est celle-ci : entiers de 1 à 9.
if (expression1 ) i = 1;
instruction1 while (i < 10)
else if (expression2 ) {
instruction2 printf("\n i = %d",i);
... i++;
else if (expressionN ) }
instructionN
else 9.2 Boucle « do---while »
instructionM
Il peut arriver que l'on ne veuille effectuer le test de continuation qu'après avoir exécuté l'instruction. Dans ce
avec un nombre quelconque de else if ( ... ). Le dernier else est toujours facultatif. La forme la plus simple est cas, on utilise la boucle do---while. Sa syntaxe est
if (expression ) do
instruction instruction
while (expression );
Chaque instruction peut être un bloc d'instructions.
Ici, instruction sera exécutée tant que expression est non nulle. Cela signifie donc que instruction est toujours
exécutée au moins une fois. Par exemple, pour saisir au clavier un entier entre 1 et 10 :
8.2 Branchement multiple « switch »
int a;
Sa forme la plus générale est celle-ci :
do
switch (expression ) {
{case constante1: printf("\n Entrez un entier entre 1 et 10 : ");
liste d'instructions 1 scanf("%d",&a);
break; }
case constante2: while ((a <= 0) || (a > 10));
liste d'instructions 2
break;
...
case constanteN: 9.3 Boucle « for »
liste d'instructions N
break; La syntaxe de for est :
default:
liste d'instructions M for (expr 1 ;expr 2 ;expr 3)
break; instruction
}
Une version équivalente plus intuitive est :
14 15
Module II20 (Remise à niveau en programmation C) S.Otmane Module II20 (Remise à niveau en programmation C) S.Otmane
On évitera toutefois ce type d'acrobaties qui n'apportent rien et rendent le programme difficilement lisible.
On a vu le rôle de l'instruction break; au sein d'une instruction de branchement multiple switch. L'instruction #include <stdio.h>
break peut, plus généralement, être employée à l'intérieur de n'importe quelle boucle. Elle permet d'interrompre
le déroulement de la boucle, et passe à la première instruction qui suit la boucle. En cas de boucles imbriquées, n'est pas nécessaire pour utiliser printf et scanf.
break fait sortir de la boucle la plus interne. Par exemple, le programme suivant :
imprime à l'écran
i=0
i=1
i=2
i=3
valeur de i à la sortie de la boucle = 3
16 17
Module II20 (Remise à niveau en programmation C) S.Otmane Module II20 (Remise à niveau en programmation C) S.Otmane
scanf("%x",&i);
Format conversion en écriture printf("i = %d\n",i);
%d int décimale signée }
%ld long int décimale signée
%u unsigned int décimale non signée Si on entre au clavier la valeur 1a, le programme affiche i = 26.
%lu unsigned long int décimale non signée
%o unsigned int octale non signée format type d'objet pointé représentation de la donnée saisie
%lo unsigned long int octale non signée %d int décimale signée
%x unsigned int hexadécimale non signée %hd short int décimale signée
%lx unsigned long int hexadécimale non signée %ld long int décimale signée
%f double décimale virgule fixe %u unsigned int décimale non signée
%lf long double décimale virgule fixe %hu unsigned short int décimale non signée
%e double décimale notation exponentielle %lu unsigned long int décimale non signée
%le long double décimale notation exponentielle %o int octale
%g double décimale, représentation la plus courte parmi %f et %e %ho short int octale
%lg long double décimale, représentation la plus courte parmi %lf et %le %lo long int octale
%c unsigned char caractère %x int hexadécimale
%s char* chaîne de caractères %hx short int hexadécimale
%lx long int hexadécimale
Table 1: Formats d'impression pour la fonction printf %f float flottante virgule fixe
%lf double flottante virgule fixe
%Lf long double flottante virgule fixe
En plus du caractère donnant le type des données, on peut éventuellement préciser certains paramètres du format %e float flottante notation exponentielle
d'impression, qui sont spécifiés entre le % et le caractère de conversion dans l'ordre suivant : %le double flottante notation exponentielle
%Le long double flottante notation exponentielle
• largeur minimale du champ d'impression : %10d spécifie qu'au moins 10 caractères seront réservés pour %g float flottante virgule fixe ou notation exponentielle
imprimer l'entier. Par défaut, la donnée sera cadrée à droite du champ. Le signe - avant le format signifie %lg double flottante virgule fixe ou notation exponentielle
que la donnée sera cadrée à gauche du champ (%-10d). %Lg long double flottante virgule fixe ou notation exponentielle
• précision : %.12f signifie qu'un flottant sera imprimé avec 12 chiffres après la virgule. De même %10.2f %c char caractère
signifie que l'on réserve 12 caractères (incluant le caractère .) pour imprimer le flottant et que 2 d'entre eux %s char* chaîne de caractères
sont destinés aux chiffres après la virgule. Lorsque la précision n'est pas spécifiée, elle correspond par
défaut à 6 chiffres après la virgule. Pour une chaîne de caractères, la précision correspond au nombre de
caractères imprimés : %30.4s signifie que l'on réserve un champ de 30 caractères pour imprimer la chaîne Table 2: Formats de saisie pour la fonction scanf
mais que seulement les 4 premiers caractères seront imprimés (suivis de 26 blancs).
Exemple : Elle retourne un int correspondant à l'entier lu ou à la constante EOF en cas d'erreur.
#include <stdio.h>
main()
{
int i;
printf("entrez un entier sous forme hexadecimale i = ");
18 19
Module II20 (Remise à niveau en programmation C) S.Otmane Module II20 (Remise à niveau en programmation C) S.Otmane
int i;
for (i = 0; i < N; i++)
printf("tab[%d] = %d\n",i,tab[i]);
}
12 Les types composés
Si le nombre de données dans la liste d'initialisation est inférieur à la dimension du tableau, seuls les premiers
A partir des types prédéfinis du C (caractères, entiers, flottants), on peut créer de nouveaux types, appelés types éléments seront initialisés. Les autres éléments seront mis à zéro si le tableau est une variable globale (extérieure
composés, qui permettent de représenter des ensembles de données organisées. à toute fonction) ou une variable locale de classe de mémorisation static.
De la même manière un tableau de caractères peut être initialisé par une liste de caractères, mais aussi par une
12.1 Les tableaux chaîne de caractères littérale. Notons que le compilateur complète toute chaîne de caractères avec un caractère
nul '\0'. Il faut donc que le tableau ait au moins un élément de plus que le nombre de caractères de la chaîne
Un tableau est un ensemble fini d'éléments de même type, stockés en mémoire à des adresses contiguës. littérale.
La déclaration d'un tableau à une dimension se fait de la façon suivante : #define N 8
type nom-du-tableau[nombre-éléments]; char tab[N] = "exemple";
où nombre-éléments est une expression constante entière positive. Par exemple, la déclaration int tab[10]; main()
indique que tab est un tableau de 10 éléments de type int. Cette déclaration alloue donc en mémoire pour l'objet {
tab un espace de 10 × 4 octets consécutifs. int i;
for (i = 0; i < N; i++)
Pour plus de clarté, il est recommandé de donner un nom à la constante nombre-éléments par une directive au printf("tab[%d] = %c\n",i,tab[i]);
préprocesseur, par exemple }
#define nombre-éléments 10
Lors d'une initialisation, il est également possible de ne pas spécifier le nombre d'éléments du tableau. Par
On accède à un élément du tableau en lui appliquant l'opérateur []. Les éléments d'un tableau sont toujours défaut, il correspondra au nombre de constantes de
numérotés de 0 à nombre-éléments -1. Le programme suivant imprime les éléments du tableau tab : la liste d'initialisation. Ainsi le programme suivant imprime le nombre de caractères du tableau tab, ici 8.
#define N 10 char tab[] = "exemple";
main() main()
{ {
int tab[N]; int i;
int i; printf("Nombre de caracteres du tableau = %d\n",sizeof(tab)/sizeof(char));
... }
for (i = 0; i < N; i++)
printf("tab[%d] = %d\n",i,tab[i]); De manière similaire, on peut déclarer un tableau à plusieurs dimensions. Par exemple, pour un tableau à deux
} dimensions :
type nom-du-tableau[nombre-lignes][nombre-colonnes]
Un tableau correspond en fait à un pointeur vers le premier élément du tableau. Ce pointeur est constant. Cela En fait, un tableau à deux dimensions est un tableau unidimensionnel dont chaque élément est lui-même un
implique en particulier qu'aucune opération globale n'est autorisée sur un tableau. Notamment, un tableau ne tableau. On accède à un élément du tableau par
peut pas figurer à gauche d'un opérateur d'affectation. Par exemple, on ne peut pas écrire ``tab1 = tab2;''. Il faut l'expression ``tableau[i][j]''. Pour initialiser un tableau à plusieurs dimensions à la compilation, on utilise une
effectuer l'affectation pour chacun des éléments du tableau : liste dont chaque élément est une liste de
constantes :
#define N 10
main() #define M 2
{ #define N 3
int tab1[N], tab2[N]; int tab[M][N] = {{1, 2, 3}, {4, 5, 6}};
int i;
... main()
for (i = 0; i < N; i++) {
tab1[i] = tab2[i]; int i, j;
} for (i = 0 ; i < M; i++)
{
On peut initialiser un tableau lors de sa déclaration par une liste de constantes de la façon suivante : for (j = 0; j < N; j++)
type nom-du-tableau[N] = {constante1,constante2,...,constanteN}; printf("tab[%d][%d]=%d\n",i,j,tab[i][j]);
}
Par exemple, on peut écrire }
#define N 4
int tab[N] = {1, 2, 3, 4};
main()
{
20 21
Module II20 (Remise à niveau en programmation C) S.Otmane Module II20 (Remise à niveau en programmation C) S.Otmane
On distingue la déclaration d'un modèle de structure de celle d'un objet de type structure correspondant à un Il est possible en C de spécifier la longueur des champs d'une structure au bit près si ce champ est de type entier
modèle donné. La déclaration d'un modèle de structure dont l'identificateur est modele suit la syntaxe suivante : (int ou unsigned int). Cela se fait en précisant le nombre de bits du champ avant le ; qui suit sa déclaration. Par
exemple, la structure suivante
struct modele
{type1 membre1; struct registre
type2 membre2; {
... unsigned int actif : 1;
typeN membreN; unsigned int valeur : 31;
}; };
Pour déclarer un objet de type structure correspondant au modèle précédent, on utilise la syntaxe : possède deux membres, actif qui est codé sur un seul bit, et valeur qui est codé sur 31 bits. Tout objet de type
struct modele objet; struct registre est donc codé sur 32 bits. Toutefois, l'ordre dans lequel les champs sont placés à l'intérieur de ce
ou bien, si le modèle n'a pas été déclaré au préalable : mot de 32 bits dépend de l'implémentation.
struct modele Le champ actif de la structure ne peut prendre que les valeurs 0 et 1. Aussi, si r est un objet de type struct
{type1 membre1; registre, l'opération r.actif += 2; ne modifie pas la valeur du champ.
type2 membre2;
... La taille d'un champ de bits doit être inférieure au nombre de bits d'un entier. Notons enfin qu'un champ de bits
typeN membreN; n'a pas d'adresse ; on ne peut donc pas lui appliquer l'opérateur &.
}objet;
On accède aux différents membres d'une structure grâce à l'opérateur membre de structure, noté ``.''. Le i-ème 12.4 Les unions
membre de objet est désigné par l'expression
objet.membre-i Une union désigne un ensemble de variables de types différents susceptibles d'occuper alternativement une
On peut effectuer sur le i-ème membre de la structure toutes les opérations valides sur des données de type type- même zone mémoire. Une union permet donc de
i. Par exemple, le programme suivant définit la structure complexe, composée de deux champs de type double ; il définir un objet comme pouvant être d'un type au choix parmi un ensemble fini de types. Si les membres d'une
calcule la norme d'un nombre complexe. union sont de longueurs différentes, la place
réservée en mémoire pour la représenter correspond à la taille du membre le plus grand.
#include <math.h>
struct complexe Les déclarations et les opérations sur les objets de type union sont les mêmes que celles sur les objets de type
{ struct. Dans l'exemple suivant, la variable hier de type union jour peut être soit un entier, soit un caractère.
double reelle;
double imaginaire; union jour
}; {
char lettre;
main() int numero;
{ };
struct complexe z;
double norme; main()
... {
norme = sqrt(z.reelle * z.reelle + z.imaginaire * z.imaginaire); union jour hier, demain;
printf("norme de (%f + i %f) = %f \n",z.reelle,z.imaginaire,norme); hier.lettre = 'J';
} printf("hier = %c\n",hier.lettre);
hier.numero = 4;
Les règles d'initialisation d'une structure lors de sa déclaration sont les mêmes que pour les tableaux. On écrit par demain.numero = (hier.numero + 2) % 7;
exemple : printf("demain = %d\n",demain.numero);
struct complexe z = {2. , 2.}; }
En ANSI C, on peut appliquer l'opérateur d'affectation aux structures (à la différence des tableaux). Dans le
contexte précédent, on peut écrire : Les unions peuvent être utiles lorsqu'on a besoin de voir un objet sous des types différents (mais en général de
même taille). Par exemple, le programme suivant
... permet de manipuler en même temps les deux champs de type unsigned int d'une structure en les identifiant à un
main() objet de type unsigned long (en
{ supposant que la taille d'un entier long est deux fois celle d'un int).
22 23
Module II20 (Remise à niveau en programmation C) S.Otmane Module II20 (Remise à niveau en programmation C) S.Otmane
main()
{
union point p1, p2, p3;
p1.coord.x = 0xf;
p1.coord.y = 0x1;
p2.coord.x = 0x8;
p2.coord.y = 0x8;
p3.mot = p1.mot ^ p2.mot;
printf("p3.coord.x = %x \t p3.coord.y = %x\n", p3.coord.x, p3.coord.y);
}
En réalité, les objets de type enum sont représentés comme des int. Les valeurs possibles constante-1, constante-
2,...,constante-n sont codées par des entiers de 0 à n-1. Par exemple, le type enum booleen défini dans le
programme suivant associe l'entier 0 à la valeur faux et l'entier 1 à la valeur vrai.
main()
{
enum booleen {faux, vrai};
enum booleen b;
b = vrai;
printf("b = %d\n",b);
}
On peut modifier le codage par défaut des valeurs de la liste lors de la déclaration du type énuméré, par exemple
:
enum booleen {faux = 12, vrai = 23};
struct complexe
{
double reelle;
double imaginaire;
};
24 25
Module II20 (Remise à niveau en programmation C) S.Otmane Module II20 (Remise à niveau en programmation C) S.Otmane
Attention : Les pointeurs et les noms de variables ont le même rôle : Ils donnent accès à un emplacement dans
13 Les pointeurs la mémoire interne de l'ordinateur. Il faut quand même bien faire la différence :
• Un pointeur est une variable qui peut 'pointer' sur différentes adresses.
• Le nom d'une variable reste toujours lié à la même adresse.
13.1 Introduction
13.2 Les opérateurs de base
Avant de parler de pointeurs, nous allons brièvement passer en revue les deux modes d'adressage principaux :
Lors du travail avec des pointeurs, nous avons besoin
Adressage direct : Dans la programmation, nous utilisons des variables pour stocker des informations. La
valeur d'une variable se trouve à un endroit spécifique dans la mémoire interne de l'ordinateur. Le nom de la - d'un opérateur 'adresse de': & pour obtenir l'adresse d'une variable.
variable nous permet alors d'accéder directement à cette valeur. - d'un opérateur 'contenu de': * pour accéder au contenu d'une adresse.
- d'une syntaxe de déclaration pour pouvoir déclarer un pointeur.
Adressage direct = Accès au contenu d'une variable par le nom de la variable.
Adressage indirect = Accès au contenu d'une variable, en passant par un pointeur P = &X; affecte l'adresse de la variable X à la variable P.
qui contient l'adresse de la variable.
P X int X , Y;
int *P ;
Soit X une variable contenant la valeur 20, Y une variable contenant la valeur 40 et P un pointeur non initialisé:
Après les instructions,
La plupart des langages de programmation offrent la possibilité d'accéder aux données dans la mémoire de
l'ordinateur à l'aide de pointeurs, c.-à-d. à l'aide de variables auxquelles on peut attribuer les adresses d'autres P = &X;
variables. Y = *P;
On peut donc définir un pointeur comme une variable spéciale qui peut contenir l'adresse d'une autre variable. *P = 100;
En C, chaque pointeur est limité à un type de données.
26 27
Module II20 (Remise à niveau en programmation C) S.Otmane Module II20 (Remise à niveau en programmation C) S.Otmane
int *P;
- P pointe sur X,
- le contenu de X (référencé par *P) est affecté à Y, et
- le contenu de X (référencé par *P) est mis à 100.
P: •
La déclaration d'un pointeur est comme suit :
<Type> *<NomPointeur>
déclare un pointeur <NomPointeur> qui peut recevoir des adresses de variables du type <Type> T: ...
peut être interprétée comme suit: Si P pointe sur une composante quelconque d'un tableau, alors P+1 pointe sur la composante suivante. Plus
généralement,
"*P-num est du type int" ou "P_num est un pointeur sur int" ou
"P_num peut contenir l'adresse d'une variable du type int" P+i pointe sur la i-ième composante derrière P et
P-i pointe sur la i-ième composante devant P.
En travaillant avec des pointeurs, nous devons observer les règles suivantes: P = T; le pointeur P pointe sur T[0], et
• Si un pointeur P pointe sur une variable X, alors *P peut être utilisé partout où on peut écrire X.
13.5 Pointeurs et chaînes de caractères
Exemple
De la même façon qu'un pointeur sur int peut contenir l'adresse d'un nombre isolé ou d'une composante d'un
Après l'instruction P = &X;
tableau, un pointeur sur char peut pointer sur un caractère isolé ou sur les éléments d'un tableau de caractères.
Un pointeur sur char peut en plus contenir l'adresse d'une chaîne de caractères constante et il peut même être
les expressions suivantes, sont équivalentes:
initialisé avec une telle adresse.
Y = *P+1 Y = X+1
*P = *P+10 X = X+10
*P += 2 X += 2 13.5.1 Affectation
++*P ++X
(*P)++ X++ On peut attribuer l'adresse d'une chaîne de caractères constante à un pointeur sur char :
Exemple
char *c;
13.4 Adressage des composantes d'un tableau c = "Salut !";
Comme nous l'avons déjà constaté, le nom d'un tableau représente l'adresse de son premier élément. En d'autre
termes:
c: •
&tableau[0] et tableau sont une seule et même adresse.
En simplifiant, nous pouvons retenir que le nom d'un tableau est un pointeur constant sur le premier élément du
tableau, par exemple : ‘S’ ‘a’ ‘l’ ‘u’ ‘t’ ‘‘ ‘!’ ‘\O’
int T[20];
28 29
Module II20 (Remise à niveau en programmation C) S.Otmane
Nous pouvons lire cette chaîne constante (par exemple : pour l'afficher), mais il n'est pas recommandé de la
modifier, parce que le résultat d'un programme qui essaie de modifier une chaîne de caractères constante n'est
pas prévisible en ANSI-C.
13.5.2 Initialisation
Un pointeur sur char peut être initialisé lors de la déclaration si on lui affecte l'adresse d'une chaîne de caractères
constante:
T est un tableau qui a exactement la grandeur pour contenir la chaîne de caractères et la terminaison '\0'. Les
caractères de la chaîne peuvent être changés, mais le nom T va toujours pointer sur la même adresse en mémoire.
C est un pointeur qui est initialisé de façon à ce qu'il pointe sur une chaîne de caractères constante stockée
quelque part en mémoire. Le pointeur peut être modifié et pointer sur autre chose. La chaîne constante peut être
lue, copiée ou affichée, mais pas modifiée.
• On n'écrit qu'une seule instruction par ligne : le point virgule d'une instruction ou d'une déclaration est
toujours le dernier caractère de la ligne.
• Les instructions sont disposées de telle façon que la structure modulaire du programme soit mise en
évidence. En particulier, une accolade ouvrante marquant le début d'un bloc doit être seule sur sa ligne.
• On laisse un blanc entre les mots-clefs if, while, do, switch et la parenthèse ouvrante qui suit, après une
virgule, de part et d'autre d'un opérateur binaire.
• On ne met pas de blanc entre un opérateur unaire et son opérande, ni entre les deux caractères d'un
opérateur d'affectation composée.
• Les instructions doivent être indentées afin que toutes les instructions d'un même bloc soient alignées. Le
mieux est d'utiliser le mode C d'Emacs (ou un autre éditeur de texte équivalent).
Références
[1] Braquelaire (J.-P.). -- Méthodologie de la programmation en C. --
Dunod, 2000, troisième édition.
[6] Kernighan (B.W.) et Richie (D.M.). -- The C programming language. -- Prentice Hall,
1988, seconde édition.
30