Sunteți pe pagina 1din 150

LANGAGE C

1
Plan
• CHAPITRE 1. ELEMENTS DE BASE
• CHAPITRE 2. LES INSTRUCTIONS
• CHAPITRE 3. LES FONCTIONS
• CHAPITRE 4. OBJETS STRUCTURES
• CHAPITRE 5. POINTEURS ET TABLEAUX
• GENIE LOGICIEL

2
Bibliographie
• Braquelaire J.P., Méthodologie de la programmation en
langage C – Principes et applications, Masson, 1993.
• Delannoy C., le livre du C – Premier langage, Eyrolles.
• Drix Ph., Langage. Norme ANSI, Masson, 1994.
• Kernighan B.W., Ritchie D.M., le langage C (ANSI),
Masson, 1992.
• Rifflet J.M., La programmation sous UNIX (chapitre 8),
Ediscience International, 1994.
• Roberts E.C. The Art and Science of C, Addison Wesley,
1995.

3
CHAPITRE I
ELEMENTS DE BASE

4
Présentation
• Le langage C a été crée par une petite équipe
d’informaticiens pour satisfaire ses besoins internes,
principalement l’écriture d’un système d’exploitation
(Unix).
• Les concepteurs de C ont notamment voulu:
– Avoir un langage C réduit et simple
– Faire confiance au programmeur

5
I. Structure générale d’un programme C

• Une suite d’un nombre quelconque d’éléments


indépendants suivants :
• Directives du préprocesseur (lignes commençant par #)
• Déclarations de types, de structures
• Déclarations de variables et de fonctions externes
• Définitions de variables
• Définitions de fonctions

6
Exemple
#include <stdio.h>
#define N 10
float calcul(float x, float y) {
float z = x+y;
return z;
}
void main( ) {
float a;
a = calcul(N, N+1);
printf(“%f”, a);
}

7
Structure générale d’un programme C

• Transformation du texte écrit en C en un programme


exécutable se fait en 2 phases :
• Compilation : traduction des fonctions écrites en C en
procédures équivalentes en langage machine. Le
compilateur lit un fichier source et produit un fichier objet.
• Edition de liens : opération par laquelle plusieurs fichiers
objet sont mis ensemble pour se compléter mutuellement :
un fichier apporte des définitions de fonctions et de
variables auxquelles un autre fichier fait référence, et
réciproquement.

8
Structure générale d’un programme C

Fichier source 1 Fichier source 2

compilateur compilateur

Fichier objet 1 Fichier objet 2

Edition de liens

Fichier exécutable

9
II. Préprocesseur

• Transforme le texte source d’un programme avant que la


compilation ne commence.
• Ne connaît pas la syntaxe du langage C.
• Transformations : ajout, suppressions et remplacements
de morceaux de texte
• Deux directives précédées du signe # :
#include
#define

10
Préprocesseur
1) Inclusion de fichiers
include est utilisé pour l’inclusion de fichiers.
• Inclusion de fichiers dans d’autres fichiers, par exple
pour insérer dans chacun des fichiers séparés qui
constituent un programme l’ensemble de leurs
déclarations communes.
• 2 formes :
#include “nom_de_fichier“
Forme générale. Le fichier spécifié est inséré à l’endroit
où la directive figure avant que la compilation ne
commence.
“nom_de_fichier“ doit être un nom complet.

11
Préprocesseur

#include <nom_de_fichier>
Ici le nom est incomplet puisqu’il s’agit d’un fichier de la
bibliothèque standard.
• Exemple
#include <stdio.h>
#include “/fall/LangageC/tri.h"

12
Préprocesseur

2) Définition et appels de macros


• Macros sans arguments
#define nom corps
Le nom de la macro est un identificateur. Le corps de la
macro s’étend jusqu’à la fin de la ligne.
Partout dans le texte où le nom de la macro apparaît, il
sera remplacé par le corps de la macro

13
Préprocesseur

• Exemple
#define PI 3.14159
#define N 100
|
|
int t[N] → int t[100]
x = 2 * PI * r → x = 2 * 3.1415 * r
Erreur souvent commise : point-virgule (;) après
#define, #include

14
Préprocesseur

• Macros avec arguments


Définition
#define nom(ident1, … , identk) corps
Appel
nom(texte1, … , textek)
Exemple
#define Valeur(p) (0.56 * (p))
Noter l’importance des parenthèses
#define Valeur(p) 0.56 *p ← 2 erreurs

15
III. ELEMENTS LEXICAUX
• Notion d’identificateur : même qu’en Pascal
Suite de lettres et de chiffres contigüs, premier-lettre
• Commentaires :
// sur une ligne
/*
*/ sur plusieurs lignes
• En C, les minuscules sont différentes des majuscules
• Quelques mots réservés
int char float double struct typedef
sizeof long short unsigned void extern
case default switch do for while else
16
ELEMENTS LEXICAUX
• Constantes entières
– Notation décimale
-20 1 360
– Notation octale (base 8). Commence par 0 (Zéro)
0761 06
– Notation hexadécimale. Commence par 0x ou 0X
0xA63 0xF56
Exemple : 3 manières d’écrire le même nombre
27 033 0x1B
• Constantes réelles
– Notation fixée
-1.64 166.7
– Notation flottante
35E8 -3.14E-10

17
ELEMENTS LEXICAUX

• Caractères et chaînes de caractères


– Un caractère est un nombre représenté sur un octet
– Une constante de type caractère se note en écrivant le caractère
entre apostrophes
– Une chaîne de caractères est une suite de caractères complétée
par le caractère nul (‘\0’)
– Une constante de type chaîne de caractères se note en écrivant
ses caractères entre guillemets
Exemples
3 caractères ‘A’ ‘2’ ‘ “’
3 chaînes de caractères “A“ “Bonjour“ “’“
“Bonjour“ → B o n j o u r \0

18
ELEMENTS LEXICAUX
• On peut faire figurer n’importe quel caractère, même non
imprimable dans une constante caractère ou chaîne de
caractères en utilisant les combinaisons suivantes
appelées séquences d’échappement :
– \n nouvelle ligne
– \t tabulation
– \a signal sonore
– \\ antislash
– \? ?
– \’ ‘
– \“ “
– \abc le caractère ayant pour code le nombre octal abc
Exemple : printf(“Bonjour\n“);
Chaîne “A\033B\”\\E” suite des caractères A, escape, B, “, \ et E 19
ELEMENTS LEXICAUX

• Exemple
#include <stdio.h>
main() {
char a, b, c, d;
a = ‘A’; b = 65; c = ‘\101’; d = ‘\x41’;
printf(‟%c %c %c % \n”, a, b, c, d);
printf(‟%c %d %o %x \n”, a, a, a, a);
}
AAAA
A 65 101 41
20
IV. TYPES FONDAMENTAUX
• Le type char
– Caractères représentés par leurs codes ASCII
– Signification d’un char en C différente de celle d’un char en
Pascal
• Le type int
– Entiers; 2 ou 4 octets ( selon les machines)
• Le type float
– Nombres réels à virgule flottante; 4 octets
• Le type double
– Nombres réels à virgule flottante en double précision; 8 octets
• Des variantes utilisent les préfixes modificateurs :
signed, unsigned, short (short int), long (long int)

21
TYPES FONDAMENTAUX
unsigned short 16 bits 0…216-1
short 16 bits -215…215-1
unsigned long 32 bits 0…232-1
long 32 bits -231…231-1
• Le type booléen n’existe pas en C
expr = 0  faux
expr ≠ 0  vrai
expr  expr « vrai »  (expr ) != 0
Exemple : x < 3 vaut 0 ou 1 selon que x est inférieur à 3 ou non

22
TYPES FONDAMENTAUX
• Déclaration de variables et initialisation
int x, y;
char ch;
float valeur;
Une variable peut être initialisée lors de sa déclaration
int t =0;
int niter = 16 * 15;
char fin = ‘f’;

23
V. LES TABLEAUX
• Tableau : arrangement de variables identiques et
contiguës
• Définition : type identificateur [taille]
la taille doit être calculable pendant la compilation
ex : int table[100];
int x, t[4], y;
t[i] = 0 ( affectation )
Remarques
1) le premier indice vaut toujours 0 (t[0])
2) taille = nombre de composantes
3) Il n’y a pas de contrôle de débordement
t = & t[0]  t = adresse de t[0]
24
LES TABLEAUX
• Tableaux à plusieurs dimensions
• Définition : type identificateur[nb_lignes][nb_colones]
• Accès identificateur[i][j]
• Exemple constantes

#define N 10
float m [N][N+2]
m[i][j]

indice ligne indice colonne

25
VI. LES EXPRESSIONS
• Expression – texte correct qui détermine une valeur
• Instruction – texte qui détermine une action à faire
• Toute expression possède au moins 2 attributs : un type
et une valeur.
exemple : Si i est un entier de valeur 10, 2*i+3 a pour
type entier et pour valeur 23.
• Deux sortes d’expressions :
– Lvalue (expressions signifiant « le contenu de »
expressions représentant un emplacement de la mémoire. La
valeur de l’expression est alors définie comme le contenu de cet
emplacement. Cas des variables, des composantes des
tableaux.
Une lvalue possède 3 attributs : adresse, type, valeur
Ex : x, t[2*i+4] 26
LES EXPRESSIONS
– Rvalue « la valeur de »
Ces expressions ont un type et une valeur, mais pas d’adresses
Ex : 2*x+3, 12
• Expressions pures et expressions à effet de bord
– Expressions pures
ex : 2*x+3
– Expressions avec effet de bord
ex : i++ (vaut i et remplace i par i+1)
y = 2*x+3 (modifie y)

27
VII. LES OPERATEURS
• Opérateurs arithmétiques : +, -, *, /, %
5/2 = 2; 5.0/2 = 2.5
• Opérateurs de comparaison : ==, !=, <, <=, >, >=
– Ils sont prioritaires par rapport à “et“ et “ou“.
• Affectation
expr 1 = expr2 (expr1 – lvalue, expr2 – rvalue)
Considérée comme une expression. Peut ainsi figurer comme
opérande dans une expression. Il est ainsi possible d’écrire en C :
i = j = k = 0  i = (j = ( k = 0))
Ex :
c = getchar( );
while ( c != ‘f’) {
t[nbcars] = c;
nbcars = nbcars +1;
c = getchar( ); 28
}
LES OPERATEURS
• L’affectation étant une sorte d’expression, ce programme
peut s’écrire :
while (( c = getchar()) != ‘f’) {
t[nbcars] = c;
nbcars = nbcars+1;
}
• Opérateurs d’affectation étendues (élargies)
C propose des combinaisons puissantes d’opérateurs,
dans le cas où l’opérande à gauche du signe= se
retrouve à droite.
expr1 += expr2  expr1 = expr1 + expr2
29
LES OPERATEURS
• De manière générale, si expr1 et expr2 sont des expressions
expr1 op= expr2  expr1 = (expr1) op (expr2)
p *= x  p = p * x
n /= 2  n = n /2
• Opérateurs d’incrémentation et de décrémentation
Il existe deux opérateurs unaires ++ différents : l’un est
postfixé (écrit derrière l’opérande), l’autre est préfixé (devant)
– Post-incrémentation
Format : exp++
exp doit être de type numérique (entier ou flottant) ou pointeur.
Cette expression a le même type que exp, le même effet de bord que
celui de l’affectation exp = exp + 1 et a la valeur de exp avant
l’évaluation de exp++
30
LES OPERATEURS
– Pré-incrémentation
Format : ++exp
Ici l’expression a la même valeur que exp après l’évaluation
Exemples
y = x++  { y = x; x = x + 1; }
y = ++x  { x = x+1; y = x; }
Il en est de même des opérateurs –
• Expressions conditionnelles
expr1 ? expr2 : expr3
expr1 est d’abord évaluée
Si sa valeur ≠ 0, expr2 est évaluée et définit la valeur de
l’expression conditionnelle. Dans ce cas, expr3 pas
évaluée. Sinon expr3 est évaluée et définit la valeur de
l’expression conditionnelle, expr2 pas évaluée. 31
LES OPERATEURS
• Exemple : y = (x != 0) ? (1/x) : maxfloat
abs = x>=0 ? x : -x
Macros avec arguments
• Les opérateurs logiques
Opérateurs && et ||
Les expressions dans lesquelles figurent ces opérateurs
sont évaluées de gauche à droite, et l’évaluation cesse
dès que la véracité ou la fausseté du résultat est établie.
&& est prioritaire sur ||, mais tous deux s’appliquent après
les opérateurs de comparaison et d’égalité
ex : if (a < b && a < c) a = 0;
32
LES OPERATEURS
• Changement de type
conversion explicite par l’opérateur de cast
(nom de type) expression
ex : (int) c;
(int)2.35 → 2
(float) 2 → 2.0
conversions utiles
1) entier → flottant
2) flottant → entier

33
LES OPERATEURS
• Opérateur sizeof
Donne la taille en octets
sizeof (type); sizeof(expression);
renvoie la taille en octets du type donné ou du type de
l’expression
sizeof(int)
sizeof(t) = taille du tableau t
sizeof(t) / sizeof(t[0]) = nombre de composantes de t
• Opérations bit à bit
Opérations non abstraites
Opération abstraite : ne fait pas intervenir les
particularités du langage 34
LES OPERATEURS
• Complément à 1 ~
ex expr 10101100
~ expr 01010011
• & et
• | ou
• ^ ou exclusif (xor)
ex 0011 0011 0011
& | ^
0101 0101 0101
0001 0111 0110

35
LES OPERATEURS
• S’appliquent à des entiers (char, int, …)
• Forcer un ou plusieurs bits à 0
char c; 13 0…01101
c = 13 & 3 3 0…00011
0…00001 -> 1
• Forcer un ou plusieurs bits à 1
char c; 13 0…01101
c = 13 | 3 3 0…00011
0…01111 -> 15
• Inverser un ou plusieurs bits
char c; 13 0…01101
c = 13 ^ 3 3 0…00011
0…01110 -> 14 36
LES OPERATEURS
• << décalage à gauche
• >> décalage à droite
ex expr1 b1b2b3b4
expr = expr1 << 1 b2b3b40
• Opérateur séquentiel (Opérateur virgule)
syntaxe e1, e2
Permet de regrouper plusieurs expressions en une seule
L’expression possède le type et la valeur de e2
Le plus souvent utilisé dans les boucles "for"

37
LES OPERATEURS
• Opérateur d’adressage
Donne l’adresse d’une variable ou d’une expression qui
est une lvalue
Format : &exp
int i, *p;
p = &i;  i et *p désignent le même objet
Utilisé dans scanf(“%d%f“, &i, &t[j]);
• Opérateur de sélection
. x.info
-> x -> info
à revoir dans les structures
38
LES OPERATEURS
Opérateur Symbole Exemple Associativité
indexation [] t[i][j] gauche
champs de structure . -> c.reel p->imag gauche
appel de fonction () max(t) gauche
négation logique ! !x droite
négation bit à bit ~ ~x droite
incrémentation ++ i++ ++i droite
décrémentation -- i-- --i droite
moins unaire - -2 droite
plus unaire + +2 droite
conversion (type) (int) droite
taille sizeof sizeof(int) sizeof(p) droite
adresse & &x droite
indirection * *p droite 39
LES OPERATEURS
Opérateur Symbole Exemple Associativité
multiplication * x*y gauche
division / x/y gauche
modulo % x%y gauche
Addition + x+y gauche
soustraction - x-y gauche
décalage à gauche << x << 4 gauche
décalage à droite >> x >> n gauche
opérateurs relationnels < <= > >= x <= 0 x > y gauche
opérateurs d’égalité- == != x ==y x != 0 gauche
inégalité
et bit à bit & x&b gauche
ou exclusif bit à bit ^ x^y gauche
ou bit à bit | x|y gauche 40
et logique && (x ==0) && (y>0) gauche
LES OPERATEURS
Opérateur Symbole Exemple Associativité
ou logique || (x ==0) || (y>0) gauche
expression conditionnelle ?: (x > 0)? x : -x droite
affectation = x =1 droite
affectation étendue += -= *= /= x %= 1 y += 1 droite
&= ^= |= z <<= 5
<<= >>=
composition d’expressions , x = 1, b = x+1; gauche
z += 10
Priorité des opérateurs : décroissante par niveau et du haut vers le bas
Associativité des opérateurs (indique l’ordre des opérations si on omet les ( ))
Associativité gauche : x+y+z = ((x+y) + z)
Associativité droite : x = y = z = (x = (y = z)) 41
IX. LES ENTREES-SORTIES
ELEMENTAIRES
#include <stdio.h> // utilisation des fonctions d’e/s
• Entrées-sorties de caractères
stdin – unité standard d’entrée → clavier
stdout – unité standard de sortie → écran
stderr – unité standard d’affichage des erreurs → écran
• Fonction getchar
Permet d’obtenir le caractère suivant du fichier courant
des entrées. Si la fin du fichier est rencontrée, la valeur
EOF est retournée

42
LES ENTREES-SORTIES
ELEMENTAIRES
int i = 0; char c;
while ((c = getchar()) != EOF && (i < 20)) {
tab[i++] = c;
}
• Fonction putchar
Permet d’envoyer le caractère précisé en paramètre dans
le fichier courant de sortie
for (i = 0; i < 10; i++)
putchar(tab[i]);

43
LES ENTREES-SORTIES
ELEMENTAIRES
• Entrées-sorties formattées
• Fonction printf
Permet d’envoyer dans le fichier courant de sortie, une
édition d’informations formattées
Syntaxe : printf(<format>, p1, p2, …, pn);
<format> - chaîne de caractères contenant du texte et
des spécificateurs de format (% suivi d’un caractère de
conversion)
%d – int %u – unsigned int char %ld – long %lu –
unsigned long
%f – double ou float (notation décimale, 6 chiffres après .) ex
1.234500
%e – double ou float (notation exponentielle, 6 chiffres après .) 44
LES ENTREES-SORTIES
ELEMENTAIRES
%c – char %s – char[ ]
printf(“Bonjour“); printf(“Resultats : %d\n”, res);
printf(“x = %d\t y = %f\n”, x, y);
Un nombre placé après % dans le code de format précise
un gabarit d’affichage, c’est-à-dire un nombre minimal
de caractères à utiliser.
printf(“%3d“,n); n=4  ☐☐4
n=35  ☐35
printf(“%10.3f“,x); x=22.5621  ☐☐☐☐22.562

45
LES ENTREES-SORTIES
ELEMENTAIRES
• Erreurs souvent commises
– Code de format en désaccord avec le type de l’expression
• Même taille  mauvaise interprétation
• Tailles différentes  peut afficher n’importe quoi
– Nombre de codes de format ≠ nombre d’expressions de la liste
• printf(“%d“, a, b);
n’affiche pas b
• printf(“%d %d “, a);
peut afficher n’importe quoi

46
LES ENTREES-SORTIES
ELEMENTAIRES
• Exemple
#include <stdio.h>
main() {
char a, b, c, d;
a = ‘A’; b = 65; c = ‘\101’; d = ‘\x41’;
printf(‟%ch! %ch! %ch! %ch! \n”, a, b, c, d);
printf(‟%c, %d %o %x \n”, a, a, a, a);
}
Ah! Ah! Ah! Ah!
A 65 101 41
47
LES ENTREES-SORTIES
ELEMENTAIRES
• Fonction scanf
Permet de rechercher des informations, selon un format
précisé dans le fichier courant des entrées
Syntaxe : scanf(<format>, &p1, &p2, …, &pn)
Même format que pour printf
%c – char, %d – int, %u – unsigned int, %hd – short int,
%hu – unsigned short, %ld – long int, %lu – unsigned
long, %f – float, %e – float, %lf – double, %le – double,
%s - chaine de caractères
int a, b; float c;
scanf(“%d%d%f“, &a, &b, &c);
48
LES ENTREES-SORTIES
ELEMENTAIRES
• L’information saisie au clavier est rangée dans un emplacement
mémoire appelée le tampon. Ce dernier est exploré par scanf
caractère par caractère. Les séparateurs blancs (dont espace et fin
de ligne jouent un rôle particulier dans les données. Il existe un
pointeur qui précise le prochain caractère à prendre
int a, b; char c;
scanf(“%d%d“, &a, &b);
53☐42⏎ a=53, b=42
☐53☐☐42☐☐⏎ a=53, b=42
53

☐42⏎ a=53, b=42
scanf(“%c%d“, &c, &a);
b75⏎ c=b, a=75
b☐☐75⏎ c=b, a=75 49
LES ENTREES-SORTIES
ELEMENTAIRES
scanf(“%d%c“, &a, &c);
25☐d⏎ a=25, c=‘ ‘
• Gabarit d’affichage
scanf(“%3d%3d“, &a, &b);
53☐42⏎ a=53, b=42
☐☐☐☐☐48523 a=485,b=23
53⏎ 42⏎ a=53, b=42
• Rôle d’un espace dans le format
– Un espace entre deux codes de format demande à scanf de faire
avancer le pointeur au prochain caractère différent d’un
séparateur
scanf(“%d☐%c“, &a, &c);
25☐d⏎ a=25, c=‘d’
25☐☐☐d⏎ a=25, c=‘d’
50
25⏎d⏎ a=25, c=‘d’
LES ENTREES-SORTIES
ELEMENTAIRES
• Entrées-sorties de lignes de caractères
• Fonction puts
Ecrit sur la sortie standard la ligne fournie en paramètre.
Le caractère ‘\n’ est automatiquement ajouté après le
dernier caractère de la ligne
puts(“Bienvenue“);

51
LES ENTREES-SORTIES
ELEMENTAIRES
• Fonction gets
Lit une ligne à partir de l’entrée standard et la mémorise
dans une zone réservée par l’appelant en la complétant
par ‘\0’.
char buf[20]; int i =0;
gets(buf);
while(buf[i] != ‘\0’) {
putchar(buf[i]);
i = i + 1;
}

52
CHAPITRE 2. LES
INSTRUCTIONS

53
LES INSTRUCTIONS
• Le bloc
Suite de déclarations et d’instructions encadrée par les
deux accolades { et }
{ déclaration … déclaration …instruction … instruction }
exemple
if (t[i] > t[i+1]) { Rq : optimisation de l’espace
int w; if (----) {
w = t[i]; int i;
t[i] = t[i+1]; …
t[i+1] = w; } else {
} float x

} 54
LES INSTRUCTIONS
• Etiquettes et instruction
Format : etiquette : instruction
une étiquette est un identificateur
goto etiquette;
transfère le contrôle à l’instruction préfixée par l’étiquette
A ne pas utiliser, sauf dans certains cas
for (--------) {
for (---- -- -)
for(---------)
if (-----) goto sortie;
….
sortie : ….
} 55
LES INSTRUCTIONS
• Instruction « if … else »
Format : if (expr) instr1 else instr2
if (expr) instr1
if (x != 0) y = A/x;
if (c >= ‘0’ && c <= ‘9’) printf("chiffre“);
• Instruction “while” et “do while”
Syntaxe while(expression)
instruction
c = ‘A’;
while(c <= ‘Z’) {
putchar(c);
c = c + 1;
} 56
LES INSTRUCTIONS
• Syntaxe do
instruction
while(expression);
instruction est exécutée au moins une fois
do {
printf(“donnez un nb > 0 :“);
scanf(“%d”, &n);
} while ( n <= 0);

• Noter la différence avec repeat…until


57
LES INSTRUCTIONS
• Instruction « for »
Format : for(expr1;expr2;expr3)
instruction
 expr1;
while(expr2) {
instruction
expr3;
}
expr1 – initialisations
expr2 – test de continuation
expr3 – expression (incrém.) 58
LES INSTRUCTIONS
int n, i, fact;
for(i = 1, fact = 1; i <= n; i++)
fact *= i;
printf(“%d ! = %d\n“, n, fact);

for ( ;expr2; )  while( expr2)


instruction instruction
for ( ; ; )
instruction
for ( ; ; ) {
printf(“Entrez un nbre ( 0 pour sortir ) : “);
scanf(“%d”, &n);
if ( n == 0) break;

}
59
LES INSTRUCTIONS
• Instruction « switch »
Format : switch(expression)
corps
Le corps prend la forme d’un bloc renfermant une suite
d’instructions de la forme
case expression-constante :
ou bien default :
switch(reponse)
{
case ‘A’ : <<traitement associé à ‘A’>> break;
case ‘B’ : <<traitement associé à ‘B’>> break;
default : <<traitement associé aux autres cas>>
} 60
LES INSTRUCTIONS
// Note entre 0 et 5
#include <stdio.h>
int main()
{
int note;
printf(“Entrez la note obtenue”);
scanf(“%d”, &note);
switch(note)
{
case 0 : puts(“Sans commentaire”); break;
case 1 : puts(“Mauvais”); break;
case 2 : puts(“Près de la moyenne”); break;
case 3 : puts(“Tu passes”); break;
case 4 : puts(“Bon travail”); break;
case 5 : puts(“Excellent”); break;
default : puts(“Note improbable”); break;
} 61
}
LES INSTRUCTIONS
int main()
{
int note;
printf(“Entrez la note obtenue”);
scanf(“%d”, &note);
switch(note)
{
case 0 :
case 1 :
case 2 :
case 3 :
case 4 :
printf(“Pas la moyenne”); break;
case 5 :
case 6 :
case 7 :
62
LES INSTRUCTIONS
case 8 :
case 9 :
case 10 :
printf(“Tu as la moyenne”); break;
default :
printf(“Erreur : note impossible”); break;
}

63
LES INSTRUCTIONS
Exemple : on compte la fréquence de chaque chiffre et des caractères blancs
dans un texte
int nb_blancs = nb_autres = 0;
for (i = 0; i < 10; i++)
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++; 64
}
LES INSTRUCTIONS
• Instructions « break » et « continue »
Dans la portée d’une structure de contrôle (for, while, do,
switch), l’instruction break provoque l’abandon de la
structure et le passage à l’instruction suivante

for(expr1; expr2;expr3) { switch(expr)


… {
break; …
… break;
} …
}
65
LES INSTRUCTIONS
continue : moins utilisée.
Abandon de l’itération courante dans une boucle, et, si la
condition l’autorise, démarrage de l’itération suivante
#define N 99
main( ) {
int n;
do {
printf(“Donnez un nbre positif“);
scanf(“%d”, &n); if (n == N) break;
if ( n < 0) { printf(“le nbre doit être positif”); continue; }
printf(“Son carré est : %d\n”, n * n);
} while(n);
}
66
LES INSTRUCTIONS
• Instruction « return »
Format : return expression;
return;
Abandon de la fonction en cours et retour à la fonction
appelante
int chercher(int x, int t[], int N) {
int i;
for(i = 0; i < N; i++)
if ( x == t[i]) return i;
return -1;
}
67
Exemple 1 : exercice d’application
1) Ecrire un programme C qui affiche les 9 tables de multiplication
pour les entiers de 1 à 9. Chaque table comporte 9 éléments, sous
la forme suivante:
1 2 3 4 5 6 7 8 9

2 4 6 8...
.
.
.
9 18 27 36 …
2) Ecrire un programme C qui affiche m tables de multiplication pour
les entiers de 1 à m avec n éléments dans chaque table. On
demandera m ≤ 20.

68
Exemple 1 : exercice d’application
#include<stdio.h>

int main() {

int i,j,n,m;

printf("Le nombre des tables: \n");

scanf("%d",&n);

/* Donner la bonne valeur de m. */

do{

printf("Le nombre de valeurs inferieur ou egal a 20: \n");


scanf("%d",&m);

} while(m>20); 69
Exemple 1 : exercice d’application
/* Impressions des tables */

printf("------------------- \n");

for(i=1;i<=n;i++) {

for(j=1;j<=m;j++) {

printf("%d \t",i*j);

}
/* Le symbol \t sert a faire des espaces blancs */

printf("\n");

printf("------------------- \n");return (1);


70
}
Exemple 2 : exercice d’application
- nombre de racines d’une équation du second degré ,
- racines de cette même équation ,

#include <stdio.h>
#include <math.h>
void main() switch(n) {
{ double a, b, c, x1, x2, delta; case 0 : printf(“Aucune racine”); break;
int n; case 1 : printf(“Une racine double: %f“, x1);break;
printf (“a = ? ”); scanf(‟%f”, &a); case 2 : printf(“Deux racines: %f %f“, x1,x2);break;
printf( “b = ? ”); scanf(‟%f” , &b); }
printf( “c = ? ”); scanf((‟%f”, &c); }
delta = b*b–4*a*c;
if (delta < 0) n=0;
else if (delta == 0) {
x1 = -b / (2 * a);
n=1; }
else {
x1 = (-b + sqrt(delta)) / (2 * a);
x2 = (-b – sqrt(delta)) / (2 * a);
n=2;
} 71
CHAPITRE 3. LES FONCTIONS

72
LES FONCTIONS
• 1) Définition, déclaration et appels
Il existe 2 syntaxes pour le C : C original et C ANSI.
Définition d’une fonction en C ANSI
type ident(declaration ident,…, declaration ident)
instruction bloc protototype de la fonct
float puissance(float x, int n) {
int i; float p = 1;
Arguments formels
for (i = 0; i < n; i++)
p *= x;
return p;
}
73
LES FONCTIONS
Arguments effectifs
• Exemple d’appel : v = puissance(5,2);
v = 2 * puissance (4*u + 5, k) + 3;

• La définition de la fonction peut être donnée avant ou après


• Dans le premier cas, pas de déclaration dans main()
• La déclaration peut se faire en écrivant :
– Soit un entête identique à celui de la définition
– Soit le prototype sans les noms des arguments formels

• Quand une fonction ne renvoie pas de valeur, on le précise


à la fois dans l’entête et dans sa déclaration par void

74
LES FONCTIONS
#include <stdio.h>
float puissance(float x, int n) {
int i; float p = 1;
for (i = 0; i < n; i++)
p *= x;
return p;
}
void main() {
float k, v;
int m;
printf(‟Calcul de la puissance: Entrez le nombre et l’exposant ”);
scanf(‟%f %d”, &k,&m);
v = puissance(k,m);
printf(‟Resultat : %f”,v);
} 75
LES FONCTIONS
#include <stdio.h>
float puissance(float,int);
void main() {
float k, v;
int m;
printf(‟Calcul de la puissance: Entrez le nombre et l’exposant ”);
scanf(‟%f %n”, &k,&m);
v = puissance(k,m);
printf(‟Resultat : %f”,v);
}
float puissance(float x, int n) {
int i; float p = 1;
for (i = 0; i < n; i++)
p *= x;
return p; 76
}
LES FONCTIONS
2) Allocation et durée de vie des variables
– Les variables globales sont toujours statiques, i.e. permanentes : existent
pendant toute la durée de l’exécution
– Les variables locales et les arguments formels sont automatiques

#include <stdio.h> appel numéro : 1 1


int i; appel numéro : 2 1
void fct(void); appel numéro : 3 1
void main( ) { appel numéro : 4 1
for(i = 1; i <= 5; i++) appel numéro : 5 1
fct( );
}
void fct(void) {
int j=0; i – variable globale
j++; j – variable locale
printf(appel numéro : %d %d\n, i,j);
} 77
LES FONCTIONS
2) Allocation et durée de vie des variables
– Les variables globales sont toujours statiques, i.e. permanentes : existent
pendant toute la durée de l’exécution
– Les variables locales et les arguments formels sont automatiques
– Le qualifieur static permet de modifier l’allocation et la durée de vie des
variables locales

#include <stdio.h> appel numéro : 1


void fct(void); appel numéro : 2
void main( ) { appel numéro : 3
int n; appel numéro : 4
for(n = 1; n <= 5; n++) appel numéro : 5
fct( );
}
void fct(void) {
static int i; Ne pas confondre variable locale
i++; statique et variable globale
printf(Appel numéro : %d\n », i);
} 78
LES FONCTIONS
3) Arguments des fonctions
• En C, le passage des arguments se fait toujours par valeur
#include <stdio.h>
void echange (int a, int b) {
int c ;
printf ("début echange : %d %d\n", a, b) ;
c=a ;
a=b ;
b=c ;
printf ("fin echange : %d %d\n", a, b) ;
}
avant appel : 10 20
main() { début echange : 10 20
int n=10, p=20 ; fin echange : 20 10
printf ("avant appel : %d %d\n", n, p) ; après appel : 10 20
echange (n, p) ;
printf ("après appel : %d %d", n, p); 79
}
LES FONCTIONS
• Arguments de type tableau
– Le nom d’un tableau, et plus généralement, toute expression
déclarée « tableau de T » est considérée comme ayant pour
type « adresse d’un T » et pour valeur l’adresse du premier
élément du tableau.
C’est ainsi que dans la déclaration d’un argument formel t de type «
tableau de T » :
– L’indication du nbre d’éléments de t est sans utilité;
– Les expressions type t[ ] et type *t sont tout à fait équivalentes
– La fonction pourra être indifféremment appelée avec un tableau
ou un pointeur pour argument effectif

80
LES FONCTIONS
int longueur(chaine de caracteres s) {
int i = 0;
while(s[i] != ‘\0’)
i++;
return i;
}
Peut être concrétisé par :
int longueur( char s[80] )
int longueur( char s[ ] )
int longueur ( char *s)
Si t est un tableau de char et p l’adresse d’un char, on peut avoir les 3 appels
l1 = longueur( t );
l2 = longueur( p );
l3 = longueur(“Bonjour“);
81
LES FONCTIONS
• Arguments par adresse
Les arguments de type simple (int, char, pointeur…) ou
bien des struct ou des unions sont toujours passés par
valeur.
C ne prévoit pas aucun mécanisme de passage par
adresse : le passage de l’adresse de l’argument doit être
programmé explicitement en utilisant comme argument
un pointeur vers la variable.

82
LES FONCTIONS
Pointeur : variable dont la valeur est l’adresse d’une cellule
de la mémoire
Exemple :
Soit A une variable contenant la valeur 10 et P un pointeur
qui contient l'adresse de A. En mémoire, A et P peuvent
se présenter comme suit:
P

… 5C26 …
3F04 3F06 3F08 A

… 10 …
5C24 5C26 5C28

83
LES FONCTIONS
Opérateur d’adressage &
P = &A;
*P -> A
Déclaration des pointeurs
Syntaxe : type * variable;
char *p;
Lorsque p aura été correctement initialisé, elle
contiendra l’adresse d’un caractère auquel on pourra
avoir accès par *p

84
LES FONCTIONS
int x, y, *px;
x = 10; y = 20;
px = &x;
y = *px + 4;
*px += 10;

Exple de passage par adresse : écrire une fonction


euclid qui calcule le quotient de la division euclidienne
d’un numérateur par un dénominateur et en plus,
calcule le reste de cette division

85
LES FONCTIONS
#include <stdio.h>
int euclid(int, int, int*);
int main( ) {
int a, b, quotient, reste;
printf(‟ Entrez les coefficients a et b ”);
scanf(‟ %d %d ”, &a, &b);
quotient = euclid( a, b, &reste );
printf(‟ quotient : %d reste : %d ”, quotient,reste);
return(0);
}
int euclid( int num, int denum, int *adreste ) {
*adreste = num % denum;
return( num / denum );
}
Déduction : les tableaux sont passés par adresse
86
Fonction retournant
plusieurs résultats
- nombre de racines d’une équation du second degré (via return),
- racines de cette même équation (via transmission par adresse),

#include <stdio.h>
#include <math.h>
int racines(double a, double b, double c, double *r1, double *r2)
{
double delta; void main()
delta = b*b–4*a*c; { double a, b, c, x1, x2, n;
if (delta < 0) return(0); printf (“a = ? ”); scanf(‟%f”, &a);
if (delta == 0) { printf( “b = ? ”); scanf(‟%f” , &b);
*r1 = -b / (2 * a); printf( “c = ? ”); scanf((‟%f”, &c);
return(1); } n = racines(a, b, c, &x1, &x2);
*r1 = (-b + sqrt(delta)) / (2 * a); switch(n) {
*r2 = (-b – sqrt(delta)) / (2 * a); case 0 : printf(“Aucune racine”); break;
return(2); case 1 : printf(“Une racine double: %f“, x1);break;
} case 2 : printf(“Deux racines: %f %f“, x1,x2);break;
}
}
87
Exemple de fonction
manipulant un tableau
#include <stdio.h>
// Prototype de la fonction d'affichage
void affiche(int *tableau, int tailleTableau);

int main()
{
int tableau[4] = {10, 15, 3, 8};

// On affiche le contenu du tableau


affiche(tableau, 4);

return 0;
}

88
Exemple de fonction
manipulant un tableau
void affiche(int *tableau, int tailleTableau)
{
int i;
for (i = 0 ; i < tailleTableau ; i++)
{
printf("%d\n", tableau[i]);
}
}
Exercice : 1) rajouter la fonction suivante, 2) rajouter une fonction de
saisie du tableau 3) reprendre l’exercice de la page 78
int chercher(int x, int t[], int N) {
int i;
for(i = 0; i < N; i++)
if ( x == t[i]) return i;
return -1;
}
89
4) rajouter ici la fonction « echange » 5) fonction de tri du tableau
LES FONCTIONS
• 4) Arguments en nombre variable
C permet de définir des fonctions dont le nombre
d’arguments n’est pas fixé et peut ne pas être le même
d’un appel à l’autre.
Une fonction avec un nombre variable d’arguments est
déclarée en explicitant quelques arguments fixes, au
moins, suivis d’une virgule et trois points.
Exple int max( short n, int x1, … )
La fonction peut être appelée avec un nombre
quelconque d’arguments effectifs, mais il faut qu’il y en
ait au moins autant qu’il y a d’arguments formels
nommés.

90
LES FONCTIONS
On utilise des macros définis dans <stdarg.h>
• va_list parcours
déclaration de la variable parcours, qui sera utilisé
comme argument dans les appels des macros va_start
et va_arg.
• va_start(parcours, dernier argument)
Initialisation de la variable parcours; dernier argument –
dernier des arguments explicitement nommés
• va _arg(parcours, type)
Parcours des arguments anonymes : le premier appel
donne le premier argument anonyme,…

91
LES FONCTIONS
#include <stdarg.h>
int max ( short n, int x1,…) {
va_list p;
int m, x, i;
m = x1;
va_start(p, x1);
for( i = 1; i < n; i++) {
x = va_arg(p, int);
if ( x > m ) m = x;
}
return m;
}
92
LES FONCTIONS
Appels de cette fonction :
a = max( 3, p, q, r);
b = max( 8, x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]);

Pour les arguments nommés, l’affectation des valeurs des


arguments effectifs aux arguments formels est faite
comme d’ordinaire.

Pour les arguments anonymes, les char et les short sont


convertis en int, les float en double

93
CHAPITRE 4. OBJETS
STRUCTURES

94
OBJETS STRUCTURES
1) Tableaux
• Définition : type identificateur [taille]
la taille doit être calculable pendant la compilation
ex : int table[100];
int x, t[4], y;
t[i] = 0 ( affectation )
Remarques
1) le premier indice vaut toujours 0 (t[0])
2) taille = nombre de composantes
3) Il n’y a pas de contrôle de débordement
t = & t[0]  t = adresse de t[0]

95
OBJETS STRUCTURES
• Tableaux à plusieurs dimensions
Définition : type identificateur[nb_lignes][nb_colones]
Accès identificateur[i][j]
Exemple
#define N 10
float m [N][N+2]
m[i][j]

96
OBJETS STRUCTURES
• Initialisation des tableaux
Une variable de type tableau peut être initialisée lors de sa déclaration
Syntaxe : { expression, expression, expression }
Ex : int t[5] = {21,13,45,3,34};
int t[] = {21,13,45,3,34};

int t1[4][4] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
int t2[4][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

1230 1234
4560 5678
7890 9000
0000 0000
t1 t2
97
OBJETS STRUCTURES
• Indexation
Soit exp une expression quelconque de type adresse et
ind une expression entière. Par définition, l’expression :
exp[ind]  * (exp + ind)
Si on a : type t[100] ou type *t;
on peut écrire : t[0] ou *t
t[i] ou *(t+i)
En C, l’identificateur d’un tableau, lorsqu’il est employé
seul, est considérée comme un pointeur (constant) sur le
début du tableau

98
OBJETS STRUCTURES
• Cas des tableaux à un indice
soit la déclaration : int t[10]
t  &t[0]
t + 1  &t[1]
t + i  &t[ i]

99
OBJETS STRUCTURES
2) Chaînes de caractères
• Pas de type chaîne de caractères,
• Tableau de caractères qui se termine par ‘\0’
char s[] = "Bonjour " ; -> B o n j o u r \0
char *s = "Bonjour " ;
Manipulation des chaînes
<stdio.h> printf, scanf, gets, puts
Ex :
char lieu[25];
int jour, mois, annee;
printf(" Entrez lieu et date de naissance : \n " );
scanf(" %s %d %d %d ",lieu, &jour, &mois, &annee);
100
OBJETS STRUCTURES
Les fonctions printf et scanf permettent de lire ou d’afficher
simultanément plusieurs informations de type quelconque. En
revanche, gets et puts ne traitent qu’une chaîne à la fois.
De plus, la délimitation de la chaîne lue ne s’effectue pas de la même
façon avec scanf et gets. Plus précisément :
● avec le code %s de scanf, on utilise les délimiteurs habituels (l’espace
ou la fin de ligne). Cela interdit donc la lecture d’une chaîne contenant des
espaces. De plus, le caractère délimiteur n’est pas consommé : il reste
disponible pour une prochaine lecture ;
● avec gets, seule la fin de ligne sert de délimiteur. De plus, contrairement
à ce qui se produit avec scanf, ce caractère est effectivement consommé :
il ne risque pas d’être pris en compte lors d’une nouvelle lecture.

Lecture de N caractères  stockage en mémoire de N+1 caractères

101
OBJETS STRUCTURES
#include <stdio.h>
void main() {
char nom[20], prenom[20], ville[25] ;
printf (”Donnez la ville de residence : ") ;
gets (ville) ;
printf (”Donnez votre nom et votre prénom : ") ;
scanf ("%s %s", nom, prenom) ;
printf ("Bonjour %s %s habitant à ", prenom, nom) ;
puts (ville) ;
}

Donnez la ville de residence : Dakar


Donner votre nom et prenom : Fall Si Fall⏎
Demba Demba  prenom : chaîne vide
Bonjour Demba Fall habitant à Dakar
102
OBJETS STRUCTURES
• Fiabiliser la lecture au clavier : le couple gets sscanf
Avec l’utilisation de « scanf », plusieurs problèmes en cas de réponse
incorrecte de l’utilisateur. Ex :
#include <stdio.h>
void main() { donnez un nombre : 12
voici son carré : 144
int n ; donnez un nombre : &
do { voici son carré : 144
printf ("donnez un nombre : ") ; donnez un nombre : voici son carré : 144
donnez un nombre : voici son carré : 144
scanf ("%d", &n) ; donnez un nombre : voici son carré : 144
printf ("voici son carré : %d\n", n*n) ; donnez un nombre : voici son carré : 144
} while (n) ; ^C
}
Arrêt prématuré de « scanf »
Valeur de retour de « scanf »: nbre de valeurs correctement lues
compte = scanf ("%d☐%d☐%c", &n, &p, &c) ;
53☐15☐d⏎ n = 53 p = 15 c = ‘d' compte = 3
53b⏎ n = 53 p inchangé c inchangé compte = 1
d⏎ n indéfini p inchangé c inchangé compte = 0 103
OBJETS STRUCTURES
• Fiabiliser la lecture au clavier : le couple gets sscanf

Pour régler ce problème :


• lecture d’une chaîne de caractères par gets (c’est-à-dire d’une suite
de caractères quelconques validés par « return ») ;
• décodage de cette chaîne suivant un format, à l’aide de la fonction
sscanf.

sscanf (adresse, format, liste_variables)


effectue sur l’emplacement dont on lui fournit l’adresse (premier
argument de typechar*) le même travail que scanf effectue sur son
tampon.
L’utilisateur est dans ce cas « maître » du tampon

104
OBJETS STRUCTURES
#include <stdio.h>
donnez un entier et un caractère : abc
#define LG 80
donnez un entier et un caractère : a 125
main()
donnez un entier et un caractère : 12
{
bonjour
int n, compte ;
merci pour 12 b
char c ;
char ligne [LG+1] ;
do
{ printf ("donnez un entier et un caractère : ") ;
gets (ligne) ;
compte = sscanf (ligne, "%d %c", &n, &c) ;
}
while (compte < 2 ) ;
printf ("merci pour %d %c\n", n, c) ;
}

105
OBJETS STRUCTURES
Dans <string.h>, les plus utilisées
s1 = s2 ne copie pas la chaîne s2 dans s1;
on utilise la fonction char* strcpy(char *s1, char*s2)
char a[12];
char b[ ] = Hello;
strcpy(a, b);
strncpy(dest, source, lg) – analogue à strcpy sur lg
caractères
Comparaison : int strcmp(char *s1, char *s2)
0 si s1 = s2
= < 0 si s1 < s2
> 0 si s1 > s2

106
OBJETS STRUCTURES
strncmp(<s>,<t>,lg) – comme strcmp mais sur lg caractères
concaténation : strcat(<s>,<t>) – ajoute <t> à la fin de <s>
strncat(<s>,<t>,n) – ajoute au plus caractères de <t> à la
fin de <s>
int strlen(char *s1) – nbre de caractères de la chaîne sans
le caractère nul

107
OBJETS STRUCTURES
• <stdlib> -> fonctions pour la conversion de nombres en
chaînes de caractères et vice-versa
Chaîne -> nombre
atoi(<s>) - retourne la valeur numérique représentée par <s>
comme int
atol(<s>) - retourne la valeur numérique représentée par <s>
comme long
atof(<s>) - retourne la valeur numérique représentée par <s>
comme double
Nombre  chaîne

108
OBJETS STRUCTURES
• Fonctions de classification et de conversion : <ctype.h>
Les fonctions de classification suivantes fournissent un résultat du
type int différent de zéro, si la condition respective est remplie, sinon
zéro.
La fonction : retourne une valeur différente de zéro,
isupper(<c>) si <c> est une majuscule ('A'...'Z')
islower(<c>) si <c> est une minuscule ('a'...'z')
isdigit(<c>) si <c> est un chiffre décimal ('0'...'9')
isalpha(<c>) si islower(<c>) ou isupper(<c>)
isalnum(<c>) si isalpha(<c>) ou isdigit(<c>)
isxdigit(<c>) si <c> est un chiffre hexadécimal
('0'...'9' ou 'A'...'F' ou 'a'...'f')
isspace(<c>) si <c> est un signe d'espacement
(' ', '\t', '\n', '\r', '\f')
109
OBJETS STRUCTURES
Les fonctions de conversion suivantes fournissent une valeur du
type int qui peut être représentée comme caractère; la valeur originale
de <c> reste inchangée:
tolower(<c>) retourne <c> converti en minuscule si <c> est une
majuscule
toupper(<c>) retourne <c> converti en majuscule si <c> est une
minuscule

110
Exemple
#include <stdio.h>
#include <string.h>
void copier(char* ch) {
char chaine[100];
int longueur = strlen(ch);
if (longueur > 99) {
printf( " la chaine est trop longue\n");
return;
}
strcpy(chaine, ch);
chaine[longueur – 1] = ‘2’;
printf( " %s\n ", chaine);
}
void main() {
copier( "Voici la chaine numero 1 ");
}
111
OBJETS STRUCTURES
3) Structures
Variables composées de types différents
Syntaxe : struct nomopt {
declaration

declaration
} (ident,…, ident)opt;
Par la suite, struct nom pourra être employé comme type
struct Article {
int numero ;
int qte ;
float prix ;
} art1, art2 ;
112
struct Article art3, art4;
OBJETS STRUCTURES
• Utilisation
art1.numero = 24;
printf( " %d\n ", art1.numero);
scanf(" %f ", &art1.prix);
art1.numero++;
• Affectation globale
Il est possible de faire une affectation globale entre structures de
mêmes types et de mêmes noms
art1 = art2; remplace
art1.numero = art2.numero ;
art1.qte = art2.qte ;
art1.prix = art2.prix ;
//se méfier avec de telles affectation lorsque les champs d’un structure
sont de types pointeur.
113
OBJETS STRUCTURES
Remarque
L’affectation globale n’est pas possible entre tableaux. Elle l’est, par
contre, entre structures. Aussi est-il possible, en créant artificiellement
une structure contenant un seul champ qui est un tableau, de réaliser
une affectation globale entre tableaux.

struct etudiant {
int numero;
char nom[30], prenom[30];
} a, b, c;
struct etudiant d, *p;
a.numero = 7;
p->numero =15;
strcpy(a.nom, “Demba“);

114
OBJETS STRUCTURES
Exercice : Affectation globale (vérification)
Ecrire un programme qui déclare deux étudiants, saisit l’un et ensuite
effectue une affectation globale à l’autre. Ensuite, le programme
affiche l’autre étudiant

• Initialisation
struct Article art1 = { 100, 200, 3000 };

struct etudiant b = {121, “Samb“, “Demba“};

115
OBJETS STRUCTURES
• Structures imbriquées
– struct Point1 {
float x;
float y;
};
struct Point1 p1;
Autres exemples de structures, imbriquées ou avec tableaux

#define N 2 Structures imbriquées


struct Point2 {
struct Rectangle1 {
float x[N]; struct Point1 phg; struct Rectangle2 {
int couleur; struct Point1 pbd; struct Point2 p[N];
}; int couleur;
}; struct Rectangle1 r1; };
struct Point2 p2; struct Rectangle2 r2;
116
OBJETS STRUCTURES
p1.x = 2.5;
p1.x++;
scanf("%f ", &p1.x);
printf("%f ", p1.x);

p2.x[0] = 1.8;
r1.phg.x = 1.7;
r2.p[1].x[0] = 3.5;
p2.couleur = 2;

117
OBJETS STRUCTURES
• Tableau de structures
struct date { int jour, mois, annee; }
struct date *p;
struct date d[4];
d[0].jour = 25;
d[2].mois = 12;
p = &d[2];
p -> jour = 25;
p++;
p -> mois = 12;
118
OBJETS STRUCTURES
• Initialisation d’un tableau de structures
struct date {
int jour,
int mois,
int annee;
} DATE [ ] = { { 31, 1, 90}, {27, 2, 91}, {29, 3, 93},
{ 28, 4, 94} };
taille d’un élément du tableau : sizeof (struct date)
taille du tableau : sizeof(DATE);
nbre d’élements du tableau : sizeof(DATE) / sizeof(struct date)
119
OBJETS STRUCTURES
• Passage d’une structure par adresse
struct S {
char nom[20];
int age;
};
void f(struct S *);
main() {
struct S st; void f (struct S *s)
strcpy(st.nom, “FALL“);
{
st.age = 60;
f(&st); strcpy(s ->nom, “DIAW“);
printf(“%s “, st.nom); } 120
}
OBJETS STRUCTURES
• Déclaration « typedef »
homologue de la déclaration type du Pascal
Syntaxe : typedef declaration
typedef float MATRICE[10][10];
MATRICE mat;  float mat[10][10];
typedef char TAMPON [80];
typedef long * PTR;
typedef struct pers {
char nom[20];
char prenom[20];
int age;
} PERSONNE;
PERSONNE p; 121
OBJETS STRUCTURES
Une fonction peut rendre des structures comme valeur
typedef struct point {
int x, y;
} POINT;
POINT milieu ( POINT X, POINT Y) {
POINT M;
M.x = (X.x + Y.x) / 2;
M.y = (X.y + Y.y) / 2;
return M;
}
Rq : il est possible de faire l’affectation entre 2 structures
122
OBJETS STRUCTURES
int main( ) {
POINT point_courant;
POINT A = {1, 2};
POINT B = {3, 4};
point_courant = milieu(A, B);
printf(“%d %d”, point_courant.x, point_courant.y);
return 0;
}
Exercice : rajouter la fonction distance qui calcule la
distance euclidienne entre deux points

123
OBJETS STRUCTURES
• 4) Champs de bits
C permet d’utiliser des structures dont les champs, tous
ou seulement certains, sont faits d ’ un nombre
quelconque de bits.
Syntaxe : { { unsigned } { int } identificateur } : entier;

Cela peut s’avérer utile :


• soit pour compacter l’information : par exemple un nombre entier
compris entre 0 et 15 pourra être rangé sur 4 bits au lieu de 16
• soit pour décortiquer le contenu d’un motif binaire, par exemple un
mot d’état en provenance d’un périphérique spécialisé

124
OBJETS STRUCTURES
struct etat {
unsigned pret : 1;
unsigned ok1 : 1;
int donnee1 : 5;
int : 3;
unsigned ok2 : 1;
int donnee2 : 4;
};
struct etat mot;

donnee2 ok2 donnee1 ok1 pret

mot.donnee1 -> entier signé avec valeurs comprises entre -24 et 24-1
125
OBJETS STRUCTURES
• 5) Les unions
Alors que les champs des structures se suivent, les
champs d’une union commencent tous au même endroit,
et donc se superposent
Syntaxe : même que celle de struct avec union à la place
union {
unsigned long m;
char *ptr;
} mot;
mot pourra être vue tantôt comme un long, tantôt comme
l’adresse d’un char

126
OBJETS STRUCTURES
Les unions peuvent être utiles pour :
• pour économiser des emplacements mémoire, en utilisant un même
emplacement pendant des phases différentes d’un même
programme;
• pour interpréter de plusieurs façons différentes un même motif
binaire. Dans ce cas, il sera alors fréquent que l’union soit elle-
même associée à des champs de bits.
Exemple
#include <stdio.h>
main() {
union essai {
long n ;
float x ;
} u;
printf ("donnez un nombre réel : ") ; 127
OBJETS STRUCTURES
scanf ("%f", &u.x) ;
printf (" en entier, cela fait : %ld", u.n) ;
}
Exemple d’exécution
donnez un nombre réel : 1.23e4
en entier, cela fait : 1178611712

128
OBJETS STRUCTURES
• Exemple d’association entre l’union et le champ de bits
union {
int valeur;
struct etat bits;
} mot;
Si m est de type int
mot.valeur = m;
Pour accéder aux différentes parties du mot m
mot.bits.pret, mot.bits.ok1, mot.bits.donnee1

129
OBJETS STRUCTURES
• 5) Enumérations
Les énumérations ne constituent pas un type structuré mais la
syntaxe de leur déclaration est similaire à celle des structures
Syntaxe : enum nomopt { id_valeur, …, id_valeur} {nom,…, nom}

enum jourouvrable {lundi, mardi, mercredi, jeudi, vendredi} x, y;


Famille finie de symboles qui représentent des constantes entières :
lundi (= 0), mardi (=1), etc…

En C, les types énumérés ne sont qu’une deuxième manière de


donner des noms à des nombres entiers
x = lundi, y = vendredi

enum booleen {faux, vrai};


enum booleen b;
b = vrai; printf(“%d\n”, b);

130
CHAPITRE 5. POINTEURS ET
TABLEAUX

131
POINTEURS ET TABLEAUX
Pointeur : variable dont la valeur est l’adresse d’une
cellule de la mémoire
Principal intérêt :
possibilité de réaliser des structures de données
récursives (listes, arbres)
moyen en C d’améliorer l’accès aux éléments des
tableaux

132
POINTEURS ET TABLEAUX
• 1) Déclaration et initialisation de pointeurs
Syntaxe : type * variable;
char *p; int *pi;
float *pf;
• Que contient un pointeur ?
– un pointeur contient une adresse
– Ex : les pointeurs pi et pf contiennent des adresses
de variables de type int et float.
– l’opérateur unaire & permet d’obtenir l’adresse d’une
variable
int i, *pi; float f, *pf;
pi = &i;
pf = &f; 133
POINTEURS ET TABLEAUX

• Lorsque pi (ou pf) aura été correctement initialisé, elle


contiendra l’adresse d’un entier (réel) auquel on pourra
avoir accès par *pi ( *pf)

• Initialisation des pointeurs


Un pointeur contient en principe une adresse valide. Il
existe 3 manières sûres de donner une telle valeur à un
pointeur :

134
POINTEURS ET TABLEAUX
– Prendre l’adresse d’une variable existant par ailleurs
type x, *p;
initialisation p = &x;
– Allocation dynamique d’un nouvel espace
type *p;
p = (type *) malloc(sizeof(type));


char * adr;
adr = (char *) malloc (20);
for (i = 0; i< 20; i++)
*(adr + i) = ‘x’;

135
POINTEURS ET TABLEAUX
long *adr;
adr = (long *) malloc( 100 * sizeof(long));
for (i = 0; i < 100; i++) *(adr + i) = i;

typedef struct elt {


char info;
struct elt * suiv;
} * PTR, ELEMENT;
PTR nouveau = (PTR) malloc (sizeof (ELEMENT));
Pour libérer l’espace, on utilise free
PTR p; free(p);

136
POINTEURS ET TABLEAUX
– Obtenir une valeur par des calculs légitimes sur des pointeurs
type t[10], *p, *q;
p = &t[0];  p = t;
q = p + 3;
Pointeurs et adresses
int x, y, *px;
x = 10; y = 20;
px = &x;
y = *px + 4;
*px += 10;

137
POINTEURS ET TABLEAUX
• Comment utiliser un pointeur ?

int x, y; Variable Adresse Contenu


int *p; x 10.000 101 / 24 / 55
1. x = 10; y 10.002 52 / 126 / 138
2. y = 5;
p 20.000 10.0003 / 10.0027
3. p = &x;

4. *p = 2;

Tant que p pointe sur x, c’est à dire que p contient l’adresse de


x, *p désigne la variable d’adresse p.
5. *p = *p + 3; // équivalent à x = x + 3;
6. y = *p + 7; // équivalent à y = x + 7;
7. p = &y;
8. (*p)++; // équivalent à y++; les parenthèses étant
importantes
138
POINTEURS ET TABLEAUX
• 2) Pointeur générique et pointeur null
Certaines fonctions de la bibliothèque, comme malloc,
rendent comme résultat une « adresse quelconque », à
charge pour l’utilisateur de la convertir.
C introduit le type void* pour représenter de telles
adresses génériques. Ce type est compatible avec tout
autre type pointeur
void * malloc (size_t size)
Pointeur nul
Valeur NULL définie dans <stdio.h>

139
POINTEURS ET TABLEAUX
• 3) Les pointeurs et les tableaux
• a) Opérations sur les pointeurs
On peut effectuer la comparaison, l’addition avec un
entier, la soustraction et les affectations de pointeurs,
mais ceux-ci doivent être de même type
Pour les affectations, exception pour le pointeur null et le
pointeur générique

int t[10]; int t[10];

for (int i = 0; i < 10; i++) int *p;

t[i] = 1; for (p = t; p < t + 10; p++)


*p = 1; 140
POINTEURS ET TABLEAUX
typedef struct point {
int x;
int y;
} POINT;
POINT t[4];
POINT *p, *q;
p = &t[1];
q = &t[3];
p -> x = 2;  t[1].x = 2;
* (q – 3). x = 8;
p < q vrai p = p-1 ou p- -;
p != q vrai q –p → 3
p >= q faux 141
POINTEURS ET TABLEAUX
• b) Indexation
Soit exp une expression quelconque de type adresse et
ind une expression entière. Par définition, l’expression :
exp[ind]  * (exp + ind)
Si on a : type t[100] ou type *t;
on peut écrire : t[0] ou *t
t[i] ou *(t+i)
En C, l’identificateur d’un tableau, lorsqu’il est employé
seul, est considérée comme un pointeur (constant) sur le
début du tableau

142
POINTEURS ET TABLEAUX
• Cas des tableaux à un indice
soit la déclaration : int t[10]
t  &t[0]
t + 1  &t[1]
t + i  &t[ i]

143
POINTEURS ET TABLEAUX
• Cas des tableaux à une dimension
int notes[4], x, *p;
1. notes[0] = 1;
2. notes[1] = notes[0] + 2;
3. notes[3] = notes[0] + notes[1];

4. p = &notes[0];

5. x = *p; // x = notes[0]; Variable Adresse Contenu


6. p = p + 1; // p = p + sizeof(int); notes[0] 10.000 11
// car « p » est de type int*
7. *p = 11; // notes[1] = 11 notes[1] 10.002 32 / 117
8. p = notes; // p = &notes[0]; notes[2] 10.004
9. x = *(p+3); // x = notes[0+3] = notes[3];
notes[3] 10.006 43
x 20.000 15 / 49
p 40.000 10.0004/
10.0026 /
144
10.0008
POINTEURS ET TABLEAUX
• Cas des tableaux à plusieurs indices
la déclaration : int t[3][4]
correspond à un tableau de 3 éléments, chacun de ses
éléments étant lui-même un tableau de 4 entiers
t + 1 correspond à l’adresse de t, augmentée de 4
entiers
t[0]  &t[0][0]
t[1]  &t[1][0]
Transmission en argument des tableaux à deux
dimensions : Pour trouver l’adresse d’un élément qque
d’un tableau à 2 indices, le compilateur ne peut plus se
contenter de son adresse de début : il doit également
connaître la seconde dimension du tableau (la première
145
n’étant pas nécessaire
POINTEURS ET TABLEAUX
• Cas des tableaux à plusieurs indices
int cases[2][6];
– cases désigne l’adresse du premier bloc de six
entiers du tableau cases ; mais c’est aussi l’adresse
du début du tableau cases.
– cases+1 désigne l’adresse du deuxième bloc de six
entiers du tableau cases.
– cases[0] et cases[1] sont de types « int* » ; elles
représentent les adresses des premiers éléments
des deux blocs de six entiers du tableau « cases ».
cases[0]  &cases[0][0]
cases[1]  &cases[1][0]
146
POINTEURS ET TABLEAUX
• Cas des tableaux à plusieurs indices

int cases[2][4]; Variable Adresse Contenu


int *p;
cases[0][0] 10.000 42
1. p = cases[0];
2. *p = 4; cases[0][1] 10.002 309
3. p = cases[1] ; cases[0][2] 10.004
4. *p = 10; cases[0][3] 10.006
5. p = cases + 1; cases[1][0] 10.008 104 / 206
6. *p = 20;
cases[1][1] 10.010
7. p = cases;
8. p++; cases[1][2] 10.012
9. *p = 30; cases[1][3] 10.014
p 20.000 10.0001 /
10.0083 /
10.0085 /
10.0007 /
10.0028 / 147
POINTEURS ET TABLEAUX
• La transmission de tableaux à une fonction
– Cas des tableaux à une dimension
Transmission par adresse : adresse de son premier élément.
#include <stdio.h>
void initialiser(int *, int);
int somme(int *, int );
void main() {
int tab[4];
initialiser(tab, 3);
cout << Somme =  << somme(tab,4); }
void initialiser(int t[], int v) {
int i;
for(i = 0; i < 4; i++) t[i] = v; // équivalent à *(t+i) = v; }
int somme(int t[ ], int n) {
int i, s;
s = 0;
for(i = 0; i < n; i++) s+= t[i];
return(s); } 148
Affichage : Somme = 12
POINTEURS ET TABLEAUX
• La transmission de tableaux à une fonction
– Cas des tableaux à deux indices
#include <iostream.h>
void initialiser(int**, int);
int somme(int**, int); Affichage
void main() { Somme = 24
int tab[2][4];
initialiser(tab, 2, 3);
cout << Somme =  << somme(tab,2); }
void initialiser(int t[ ][4], int n, int v) {
int i, j;
for(i = 0; i < n; i++)
for(j = 0; j < 4; j++)
t[i][j] = v; }
int somme(int t[][4], int n) {
int i, j, s;
s = 0;
for(i = 0; i < n; i++)
for(j = 0; j < 4; j++)
s+= t[i][j]; return(s); } 149
POINTEURS ET TABLEAUX
• Exemples relatifs à l’indexation
char s[10] =  Hello;
main() {
char *p = s;
while (*p != ‘\0’)
putchar(*p++);
}
int tasser (int t[ ], int n) {
int tasser (int t[ ], int n) {
int srce, dest; int *srce, *dest;
srce = dest = 0; srce = dest =t;
while( n-- > 0) { while (n-- > 0) {
if (t[srce] != 0) if (*srce != 0)
t[dest++] = t[srce]; *dest++ = *srce;
srce ++ ; srce ++;
} }
return dest; return dest – t;
} 150
}

S-ar putea să vă placă și