Langage C 2025
Langage C 2025
Prof. A. SABOUR
1
Objectifs du cours ...
Objectif :
Ce cours a pour objectif de procurer une connaissance moderne de la
programmation afin qu’un étudiant puisse résoudre des problèmes.
Le langage de programmation utilisé est le C ANSI 89.
De façon plus spécifique, ce cours devra permettre à l’étudiant de :
acquérir les notions de programmation de base;
acquérir une connaissance du langage C ;
Être capable de traduire un algorithme en un programme C
Comprendre les différentes constructions de la programmation en C
2
Historique
• 1972 : Dennis Richie & Ken Thompson (Bell Labs) pour développer le
système UNIX
• 1980 : C devient populaire plusieurs compilateurs
• 1983 : ANSI (American National Standards Institute) normalise le
langage
• 1989 : fin de la normalisation qui donne la norme ANSI C
3
Généralités
• Langage successeur du langage B dans les années 60, premières normes en 1978 puis
norme ANSI en 1989 et ISO en 1990.
• C n'est lie a aucune architecture particulière ;
• C est un langage type qui fournit toutes les instructions nécessaires a la programmation
structurée ;
• C est un langage compile.(et non interprété)
• Usage des pointeurs, récursivité
• Langage polyvalent permettant le développement de systèmes d'exploitation, de
programmes applicatifs scientifiques et de gestion.
• Langage évolué qui permet néanmoins d'effectuer des opérations de bas niveau.
• Portabilité (en respectant la norme !) due à l'emploi de bibliothèques dans lesquelles
sont reléguées les fonctionnalités liées à la machine.
• Grande efficacité et puissance. 4
C est un langage de bas niveau
• Il n'est pas rare d'entendre dire que C est un assembleur de haut niveau i.e.
un assembleur type qui offre des structures de contrôle élaborées et qui est –
relativement- portable (et porte) sur l'ensemble des architectures.
5
La fonction principal
6
La fonction principal
TYPE de la valeur de
retour "main" : Cela signifie "principale", ses
instructions sont exécutées.
int main(void)
{
/* corps du programme*/ void main(void): La fonction main ne prend
debut aucun paramètre et ne retourne pas de valeur.
declaration des Cstes et Var ;
instruction1 ; int main(void): La fonction main retourne une
instruction2 ; valeur entière à l'aide de l'instruction return (0
…. si pas d’erreur).
} int main(int argc, char *argv[]): On obtient
fin alors des programmes auxquels on peut adresser
des arguments au moment où on lance le
programme.
Entre accolades "{" et "}" on
mettra la succession
d'actions à réaliser.(Bloc)
7
Structure d ’un programme
8
Les composantes élémentaires du C
• Un programme en C est constitué de 6 composantes
élémentaires :
• identificateurs
• mots-clefs
• constantes
• chaînes de caractères
• opérateurs
• signes de ponctuation
• + les commentaires
9
Identificateurs
• Un identificateur peut désigner :
• Nom de variable ou fonction
• type défini par typedef, struct, union ou enum,
• étiquette
• un identificateur est une suite de caractères :
• lettres, chiffres, « blanc souligné » (_)
n’est jamais un chiffre
• Premier caractère
• minuscules et majuscules sont différenciées
• Longueur<=31
Identificateurs valides :
xx y1 somme_5 _position
Noms surface fin_de_fichier VECTEUR
Identificateurs invalides :
3eme commence par un chiffre
x#y caractère non autorisé (#)
no-commande caractère non autorisé (-)
taux change caractère non autorisé (espace)
12
Types de base
13
Mots-clefs
• Réservés pour le langage lui-même et ne peuvent être utilisés comme
identificateur, 32 mots-clefs :
• const, double, int, float, else, if, etc.
Commentaires
14
Un identificateur ne peut pas être un mot réservé du langage :
16
Les 40 operateurs de l'ansi C
• les operateurs
17
Structure d ’un programme C
• Une expression est une suite de composants élémentaires syntaxiquement correcte,
par exemple :
x=0
(i>=0) && (i<10) && (p !=0)
• Une instruction est une expression suivie d ’un point-virgule (fin de l’instruction)
18
Structure d ’un programme C
19
Qu’est-ce qu’un bloc d ’instructions ?
20
Structure d ’un programme C
• Une instruction composée d ’un spécificateur de type et d ’une liste
d ’identificateurs séparés par une virgule est une déclaration, par
exemple :
int a;
int b = 1, c;
char message[80];
21
Structure d ’un programme C
Un programme C se présente de la façon suivante :
[ directives au préprocesseur ]
[ déclarations de variables externes ]
[ fonctions secondaires ]
main ()
{
déclarations de variables internes
instructions
}
22
Préprocesseur
• Le préprocesseur effectue un prétraitement du programme source avant qu'il soit compilé.
• Inclusion de fichiers
#include <nom-de-fichier> /* répertoire standard */
#include "nom-de-fichier" /* répertoire courant */
/*
* RAND_MAX is the maximum value that may be returned by rand.
* The minimum is zero.
*/
#define RAND_MAX 0x7FFF
/*
* These values may be used as exit status codes.
*/
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
#define __need_size_t
#define __need_wchar_t
24
Quelques lignes du fichier stdlib.h
Directives du préprocesseur
#ifndef _STDLIB_H_
#define _STDLIB_H_
….
#endif
#if !defined (__STRICT_ANSI__)
#else
Quelques lignes du fichier stdlib.h
25
1er Programme
26
La fonction printf()
Librairie : stdio.h #include <stdio.h>
Exemple :
printf("Qu'il est agreable d’utiliser printf en\t C,\nlorsqu'on l'utilise
\"proprement\".\n");
28
La fonction scanf()
Librairie : stdio.h. #include <stdio.h>
30
Scanf : format
• Format
chaîne de caractères composée de caractères % suivis d'une lettre et
éventuellement séparés par des blancs
la lettre indique le type de conversion à effectuer
exemple :
int i; float x;
scanf("%d %f", &i,&x);
31
Scanf : format
• Exemple
char t[20];
int i ; float x;
scanf ("%s %d %f", t,&i,&x);
réponses :
1/ abcde 123 0.05
2/ abcde 123
0.05
3/ abcde
123
0.05
32
Scanf : rôle des caractères , , tabulation, dans les réponses
• Dans les réponses
, , tabulation servent de délimiteurs pour les valeurs numériques et les chaînes
de caractères (pas pour les caractères)
• Exemples
scanf ("%d%f",&i,&x);
rep1 : 123 456 i = 123 , x = 456.0
rep2 : 123456 i = 123456 , x : pas encore lu (en attente)
scanf("%s%d",ch,&i);
rep : abc 12 ch = "abc" , i=12
scanf ("%c%c",&c1,&c2);
rep1 : ab c1= 'a' , c2 = 'b'
rep2 : ab c1= 'a' , c2 =
scanf ("%c%c%c",&c1,&c2,&c3);
rep1 : ab c1= 'a' , c2 = 'b', c3=
rep2 : ab c1= 'a' , c2 = 'b'
c c3 =
33
Scanf : rôle des caractères et tabulation, dans la chaîne de format
scanf ("%c%c%c",&c1,&c2,&c3);
rep1 : abc c1= 'a' , c2 = 'b', c3= 'c'
rep2 : abc c1= 'a' , c2 = 'b', c3= 'c'
rep2 : ab
c c1= 'a' , c2 = 'b', c3= 'c'
34
Scanf : compléments
• Nombre de caractères lus
faire précéder le caractère de format du nombre de caractères (max) désiré
Exemples
int i,j,k;
scanf("%3d %3d %3d",&i,&j,&k);
rep1 : 123 i=1 j=2 k=3
rep2 : 123456789 i=123 j=456 k=789
rep3 : 123456789 i=123 j=456 k=789
rep4 :123456789 i=123 j=4 k=567
35
Scanf : compléments
• Lecture d'une chaîne de caractères
char ch[50];
scanf("%s",ch) // pas de &
rep : abcdefghi
'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 0 ? ?
Le caractère 0 de fin de chaîne est ajouté automatiquement
Exercice : faire l'équivalent de scanf("%s",ch) à l'aide de getchar()
36
scanf : compléments
• Filtre sur chaînes de caractères
[ caractères admissibles] ou [^caractères non admissibles]
exemples
char ch[100];
scanf("%[0123456789]",ch);
rep : 32a48 ch="32"
scanf("%[^0123456789]",ch);
rep : 32a48 ch="a"
37
Affichages et saisies
Librairie : stdio.h
Fonction Syntaxe Description
int main()
{
char c; 22 123.3333 s
int i;
22
float x;
123.300003
scanf("%2d %5f %c",&i,&x,&c); 3
printf(" \n %d\n %f\n %c ",i,x,c);
}
39
Opérateurs d'adressage
• Adresse de : &
Syntaxe : &variable , donne l'adresse mémoire de la variable
Exemple :
int i,adr;
adr = &i;
ne pas confondre avec le "et" bit à bit
40
Opérateur de taille : sizeof
• Donne la taille de l'implantation
• 2 syntaxes
1/ sizeof expression
exemple :
int i,j ;
j= sizeof i ; -> 2 ou 4 (octets)
2/ sizeof(type)
exmples :
typedef char tab[100];
tab t;
int n;
n = sizeof(int), -> 2 ou 4 (octets)
n = sizeof(tab) -> 100 (char)
41
Opérateurs et expressions
• Opérateurs à un paramètre:
• - change le signe de la variable
• ~ complément à 1
• * « indirection » (pointeurs)
• value = *salary; /* contenu de la variable pointé par salaire */
• &adresse
• int old,*val = &old;
• ++/-- incrémentation/décrémentation
• sizeof()
• printf(" \n char %d ",sizeof( char));
• printf(" \n long int %d ",sizeof( long int));
• printf(" \n int %d ",sizeof( int));
• printf(" \n short int %d ",sizeof( short int));
• printf(" \n short %d ",sizeof( short ));
7.0/2
7/2.0
7.0/2.0
3.5
43
Opérateurs et expressions
Les opérateurs de comparaison
< plus petit Le type booléen
<= plus petit ou égal n'existe pas. Le
résultat d'une
> plus grand
expression logique
>= plus grand ou égal vaut 1 si elle est
== égal vraie et 0 sinon.
!= différent
Réciproquement,
Les opérateurs logiques toute valeur non
nulle est considérée
&& et comme vraie et la
valeur nulle comme
|| ou
fausse.
! non 44
Exemple
int i;
float f;
char c;
i = 7; f = 5.5; c = 'w';
!expr1 : est vrai si expr1 est faux et faux si expr1 est vrai ;
expr1&&expr2 est vrai si les deux expressions expr1 et expr2 sont vraies et faux sinon.
L'expression expr2 n'est évaluée que dans le cas où l'expression expr1 est vraie ;
expr1 || expr2 est vrai si expr1 est vrai ou expr2 est vrai. et faux sinon.
L'expression expr2 n'est évaluée que dans le cas où l'expression expr1 est fausse.
45
Opérateurs et expressions
Contractions d'opérateurs
• Il y a une famille d’opérateurs
+= -= *= /= %=
&= |= ^=
<<= >>=
• Pour chacun d’entre eux
expression1 op= expression2
est équivalent à:
(expression1) = (expression1) op (expression2)
a += 32; f /= 9.2; i *= j + 5;
47
Décalages
• Décalages
• à gauche a << b : a est décalé à gauche de b bits (les bits ajoutés valent 0)
5 << 2 0000 0000 0001 0100 20
un décalage d'une position à gauche correspond à une multiplication par 2
• à droite a >>b : a est décalé à droite de b bits (les bits insérés valent le
bit de poids fort)
14 0000 0000 0000 1110
14 >> 2 0000 0000 0000 0011 3
-6 1111 1111 1111 1010
-6 >> 1 1111 1111 1111 1101 -3
un décalage d'une position à droite correspond à une division par 2 (en
respectant le signe)
48
Opérateurs et expressions
Incrément et décrement
• C a deux opérateurs spéciaux pour incrémenter (ajouter 1) et décrémenter
(retirer 1) des variables entières
++ increment : i++ ou ++i est équivalent à i += 1 ou i = i + 1
-- decrement
• Ces opérateurs peuvent être préfixés (avant la variable) ou postfixés (après)
int i = 5, j = 4;
“i” vaudra 6
i++;
“j” vaudra 3 --j;
++i;
“i” vaudra 7 49
Opérateurs et expressions
Préfixe et Postfixe
#include <stdio.h>
int main(void)
{
int i, j = 3; équivalent à:
1. j++;
i = ++j; 2. i = j;
printf("i=%d, j=%d\n", i, j);
j = 3; équivalent à:
i = j++; 1. i = j;
printf("i=%d, j=%d\n", i, j); 2. j++;
return 0;
} i=4, j=4
i=3, j=4
50
Opérateurs divers
• ( ) : force l'ordre des calculs
ex : 1 + 2 * 3 -> 7
(1+2) * 3 -> 9
• [ ] pour les tableaux
t[2] équivalent à *(t+2)
51
Priorité des opérateurs
Priorité Opérateurs Description Associativité
15 () [ ] -> . opérateurs d'adressage ->
++ -- incrément/décrément
~ complément à un (bit à bit)
! non unaire
14 <-
&* adresse et valeur (pointeurs)
(type) conversion de type (cast)
+- plus/moins unaire (signe)
13 */% opérations arithmétiques ->
12 +- "" ->
11 << >> décalage bit à bit ->
10 < <= > >= opérateur relationnels ->
9 == != "" ->
8 & et bit à bit ->
7 ^ ou exclusif bit à bit ->
6 | ou bit à bit ->
5 && et logique ->
4 || ou logique ->
3 ?: conditionnel <-
= += -= *= /= %=
2 assignations <-
>>= <<= &= ^= |=
1 , séparateur -> 52
Priorité des opérateurs
a – b /c *d
(a-b) / (c-d)
i = j = k = 0;
!0 == ++0
1 == 1
a=-1; (!a==++a)? printf(" !a==++a"): printf("Noonn !a==++a");
printf("\n %d %d ", 2/2*31/2, 60*2/31*2);
printf("\n %d %d ", 2/2/31/2, 90/2/31*2);
printf("\n %d %d ", 20/2%31/2, 90%2/31*2);
printf("\n %d %d ", 20<<2%31/2, 100%30>>2);
53
Priorité des opérateurs (exercices)
main(){
int x, y , z;
x = 2;
x += 3 + 2; printf("%d\n",x);
x -= y = z = 4; printf("%d%d%d\n",x,y,z);
x = y == z; printf("%d%d%d\n",x,y,z);
x == (y = z); printf("%d%d%d\n",x,y,z);
x = 3; y =2 ; z = 1;
x = x && y || z ; printf("%d\n", x);
printf ("%d\n", x || ! y && z);
x = y = 0;
z = x ++ -1; printf ("%d, %d\n", x, z);
z += -x ++ + ++ y; printf ("%d, %d\n", x, z);
x =1 ; y =1;
printf("%d\n", ! x | x);
printf("%d\n", ~ x | x);
printf("%d\n", x ^ x);
x <<= 3 ; printf("%d\n", x);
y <<= 3 ; printf("%d\n", y);
y >>= 3 ; printf("%d\n", y);
54
Priorité des opérateurs (exercices)
x =0 ; y =0; z=0;
x+=y+=z;
printf("%d\n", x < y ? y : x) ;
printf("%d\n", x < y ? x++ : y++) ;
printf("%d, %d\n", x , y);
printf("%d\n", z += x < y ? x++ : y++) ;
printf("%d, %d\n", y , z);
x = 3; y = z = 4;
printf("%d\n",( z >= y >= x) ? 1 : 0) ;
printf("%d\n", z >= y && y >= x ) ;
x = y = z = 0;
}
55
Opérateurs et expressions
• Opérateur conditionnel:
L’instruction suivante :
Est équivalante à :
if (mode>0)
result=1;
else
result=0;
56
Les structures de contrôle en C
Alternative: if-else
Choix Multiple: switch-case
Itérations: for, while, do-while
Rupture de Contrôle: break, continue, return …
goto
57
Les tests
• Syntaxes :
if (expression_test)
bloc_instructions_1
if (expression_test)
bloc_instructions_1
else
bloc_instructions_2
58
Tests (suite)
• Enchaînement de if :
if (expression_test1)
bloc_d_instructions_1
else if (expression_test2)
bloc_d_instructions_2
else if (expression_test3)
bloc_d_instructions_3
...
else
bloc_d_instructions_final
59
Tests (suite)
• else est associé avec le if le plus proche
int i = 100;
if(i > 0)
if(i > 1000)
printf("i > 1000\n");
else
printf("i is reasonable\n");
int i = 100;
if(i > 0) {
if(i > 1000)
printf(" i > 1000 \n");
} else
printf("i is negative\n");
60
Expressions évaluées dans les tests
• if (a = b) : erreur fréquente, expression toujours vraie !
• if (a == b) : a égal à b
• if (a != b) : a différent de b
• if (a > b) : a supérieur à b
• if ((a >=b)&&(a>0)) : a supérieur ou égal à b et a positif
• if ((a<=b)||(a>0)) : a inférieur ou égal à b ou a positif
• Tout ce qui est 0 ( ‘\0’ 0 0.0000 NULL ) est faux
• Tout ce qui est != de 0 ( 1 ‘0’ 0.0001 1.34 ) est vrai
• if(32) printf("ceci sera toujours affiche\n"); if (a<b) {
min=a;
• if(0) printf("ceci ne sera jamais affiche\n");
}
else {
if(delta != 0) ⇔ if(delta) min=b;
if(delta == 0) ⇔ if(!delta) }
61
Exemples :
if (i < 10) i++;
La variable i ne sera incrémentée que si elle a une valeur inférieure à 10.
if (i == 10) i++; == et pas =
La variable i ne sera incrémentée que si elle est égale à 10.
if (!recu) printf ("rien reçu\n");
Le message "rien reçu" est affiché si recu vaut zéro.
if ((!recu) && (i < 10)) i++;
i ne sera incrémentée que si recu vaut zéro et i<10.
Si plusieurs instructions, il faut les mettre entre accolades.
62
Boucle « for »
• La boucle for :
for (initialisation ; test ; instruction) {
instructions;
}
• Exemple :
for (i = 0 ; i <= 50 ; i++) {
printf(" i = %d\n ",i);
}
« Commencer à i =0, tant que i <= 50 , exécuter l ’instruction printf
et incrémenter i »
63
Boucle « for »
/* Boucle for */
#include <stdio.h>
#define NUMBER 22
main()
{ Initialisation Condition de fin
int count, total = 0; de boucle
for(count =1; count <= NUMBER; count++, total += count)
printf(“Vive le langage C !!!\n”);
printf(“Le total est %d\n”, total);
}
64
Exemples
double angle;
int i, j, k;
for( ; ; )
{
............; /* bloc d'instructions */
............;
............;
}
est une boucle infinie (répétition infinie du bloc d'instructions).
65
Boucle while
• La boucle while :
while(test) {
instructions;
}
• Le test se fait d'abord, le bloc d'instructions n'est pas forcément exécuté.
• Rq: les {} ne sont pas nécessaires lorsque le bloc ne comporte qu'une seule instruction.
• exemple
…
int i;
i = 0;
while (i < 10)
{
printf("i = %d \n ",i);
i++;
}
…
« Tant que i est inférieur à 10, écrire i à l ’écran, incrémenter i » 66
Boucle do … while bloc d'
instructions
• La boucle do … while :
do {
instructions; non
condition
} while (test); oui vraie
suite du programme
• permet d ’exécuter au moins une fois les instructions
avant d ’évaluer le test
67
Boucle « while »
/* Boucle while */
#include <stdio.h>
#define NUMBER 22
main() Initialisation
{
int count = 1, total = 0;
Condition de fin de boucle (boucle
while(count <= NUMBER) tant que vrai)
{ (boucle faite que si vrai)
printf(“Vive le langage C !!!\n”);
count++;
total += count;
}
printf(“Le total est %d\n”, total);
} Incrémentation
68
Boucle do … while
/* Boucle do while */
#include <stdio.h>
#define NUMBER 22
main() Initialisation
{
int count = 1, total = 0;
do
{
printf(“Vive le langage C !!!\n”);
count++; Incrémentation
total += count;
} while(count <= NUMBER); Condition de fin de boucle (boucle
printf(“Le total est %d\n”, total); tant que vrai)
} (boucle faite au moins 1 fois)
69
Choix multiple: switch case
/* Utilisation de switch case */
main() Paramètre de décision
{
char choix;
… Exécuté si choix = a
switch(choix)
{
case ‘a’ : fonctionA(); Exécuté si choix = a ou b
case ‘b’ : fonctionB();
Exécuté si choix = a, b ou c
case ‘c’ : fonctionC();
default : erreur(3);
} Exécuté si choix non répertorié
} par un « case »
et si choix = a, b ou c
70
Effet du « break »
/* Utilisation de switch case */
main() Paramètre de décision
{
char choix;
… Exécuté si choix = a
switch(choix)
{ Exécuté si choix = b
case ‘a’ : fonctionA(); break;
Break;
# Continue; 8
int i, j=1; for (i = -10; i <= 10; i++)
char a; {
for (i = -10; i <= 10; i++){ if (i == 0)
continue;
while(j!=0) /* boucle infinie */ // pour éviter la division par zéro
{ printf(“ %f”, 1 / i);
a=getchar(); }
if(a= ='x')
break;
} return (expression);
} permet de sortir de la fonction qui la
contient
Si x est tapée au clavier
exit (expression); La fonction est interrompu.
expression : un entier indiquant le code de terminaison
72
du processus
goto étiquette
1 #include <stdio.h>
2 void main()
3 {
4 int i, j;
5 for (i=0; i < 10; i++)
6 for (j=0; j < 4; j++) {
7 if ( (i*j) == 10)
8 goto trouve;
9 printf("i*j != 10.\n");
10 }
11 trouve:
12 printf("i*j =%d * %d = %d== 10.\n",i,j,i*j);
13 }
73
Langage C
Les Tableaux && Les Fonctions
Prof. A.SABOUR
74
Objectifs du cours ...
Objectif :
• Être capable de manipuler les Tableaux et les fonctions
75
Tableaux
• Lorsque on veut mémoriser plusieurs données de même type, on peut utiliser un tableau c-à-d on
regroupe sous un même nom plusieurs informations
• Exemple de déclaration d'un tableau d'entiers
int tab[100];
int : type des éléments du tableau
tab : identificateur (nom du tableau)
100 : nombre d'éléments du tableau (dimension)
76
Tableaux
• Utilisation
chaque élément du tableau est accessible par un indice qui doit être de
type entier, quelque soit le type des éléments du tableau
exemples :
int i ;
tab[2] 3eme élément du tableau
tab[2+3] 6eme élément du tableau
tab[i] i+1eme élément du tableau
• Exemples :
stocker les 100 premiers nombres pairs : 0,2,4,...,196,198
int i, t[100];
for (i=0; i < 100; i=i+1)
t[i]= 2*i;
77
Tableaux
• Remarques:
1/ chaque élément du tableau s'utilise comme une variable
tab[3] = 2;
78
Parcours des éléments d’un tableau
Parcours du premier au dernier
int i; /* l’indice de balayge doit être un entier */
float t[100]; /* le type du tableau est quelconque */
for (i=0; i < 100; i=i+1) // ou bien for (i=0; i <= 99; i=i+1)
t[i]= …….;
79
La dimension
Bonne pratique de programmation
int i;
int t[100];
for (i=0; i < 100; i=i+1)
t[i]= 100;
Pb : changement de la taille du tableau ….
80
Exemples
Point de l'espace
1ere solution :
float x,y,z;
2eme solution
float pt[3];
// pt[0] pour x, pt[1] pour y, pt[2] pour z
81
Chaînes de caractères
• En c, pas de type prédéfini chaîne de caractères. En pratique on utilise des
tableaux de caractères.
• Convention : le dernier caractère utile est suivi du caractère \0 (de code ascii
0)
• Exemples :
char t[10]; (9 caractères max, puisque une case réservée pour \0;
'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' ‘\0’
strcpy(t,"abcdefghi");
chaque lettre est accessible par l'indice
char t[12]; 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' ‘\0’ ? ?
strcpy(t,"abcdefghi");
• Initialisation char t[12]= "abcdefghi"; ou char t[]= "abcdefghi";
83
Les tableaux
Rappel : tableau =regroupement de données de même type sous un même nom,
accessibles par un indice (0,..,dim-1)
t[0] t
L'adresse de la première case est t t[1]
t[2]
&t[0] <==> t
t[48]
*t <==> t[0] t[49]
84
Tableaux
• Initialisation à la compilation
1 2 3 4 5 6 7 8 9 10
int t[10] = {1,2,3,4,5,6,7,8,9,10};
0. 0.25 3.14 2.57
float x[4] = {0.,0.25,3.14,2.57};
char couleur[4]= {'r','v','b','j'}; r v b j
char texte[10]="abcd"; a b c d \0 ? ? ? ? ?
int t1[10] = {1,2,3};
1 2 3 ? ? ? ? ? ? ?
85
Tableaux
• Accès aux éléments d'un tableau
int t[50];
syntaxe 1
// accès à la (i+1)ème case avec i compris entre 0 et 49
t[i];
syntaxe 2
puisque t est l'adresse de la iere case
t[0] <==> *t // mot d'adresse t, * : opérateur mot dont l'adresse est)
t[1] <==> *(t+1) // rem : priorité des opérateurs)
…
t[i] <==> *(t+i) // *t+i <==> t[0]+i
86
Tableaux à plusieurs dimensions
• Tableau dont chaque case est elle-même un tableau
ex :
typedef int t[100] ; // t est un type
t matrice [20];
matrice est un tableau de 20 cases, chacune est un tableau de 100 entiers
=> matrice est un tableau de 20*100 entiers
autre déclaration :
int matrice [20][100]; // tableau de 20 "lignes" et 100 "colonnes"
87
Tableaux à plusieurs dimensions
• Pas de limitations sur le nombre de dimensions
Ex à 3 dimensions : tableau de coord. de pts de l'espace
typedef float point[3] ; // x: indice 0, y : indice 1, z : indice 2
point tab[100][100]; // tab = matrice de 100 points
ou bien
tab[100][100][3];
t[0][0]
t[0][1] t[0]
• Implantation mémoire
t[1][0]
t[1]
int t[3][2]; t[1][1]
t[2][0]
t[2][1] t[2]
88
Tableaux à plusieurs dimensions
• Initialisation (à la compilation) t[0][0] = 1
t[0][1] = 2 t[0]
int t[3][2] = {1,2,3,4,5,6}; t[1][0] = 3
t[1]
t[1][1] = 4
ou bien (+ clair) t[2][0] = 5
t[2][1] = 6 t[2]
int t[3][2] = {{1,2},{3,4},{5,6}};
89
Tableaux à plusieurs dimensions
• Accés aux éléments
int t[dim1][dim2] ;
t[i][j] <==> * (*t +i*dim2+j)
int t[dim1][dim2][dim3];
t[i][j][k] <==> * (**t +i*dim2*dim3+j*dim3+k)
int t[dim1][dim2]….[dimn] ;
t[i1][i2]….[in] <==> * (*….*t // n-1 *
+i1*dim2*dim3*dim4….. *dimn
+i2* dim3*dim4….. *dimn
+…..
+in-1 *dimn
+in) )
=> la première dimension n'est pas utilisée dans le calcul
90
Tableaux à plusieurs dimensions
#include <stdio.h>
#include <stdio.h> int main(void)
{
Lorsque l'on exécute un programme, celui-ci est stocké en mémoire, cela signifie que d'une part le code à
exécuter est stocké, mais aussi que chaque variable que l'on a défini a une zone de mémoire qui lui est réservée,
En réalité la mémoire est constituée de plein de petites cases de 8 bits (un octet). Une variable, selon son type
(donc sa taille), va ainsi occuper une ou plusieurs de ces cases. Chacune de ces « cases » (appelées blocs) est
92
Adresse
L’adresse (son emplacement en mémoire) d’une variable est accessible par l'opérateur &
(adresse de)
ex : scanf((“%d”,&unEntier);
scanf a besoin de l’adresse en mémoire de la variable unEntier pour y placer la valeur lue
93
Taille d’un emplacement en mémoire
Pour connaître la taille d’un emplacement en mémoire, nous utilisons l’opérateur sizeof( );
Le paramètre passé est soit un type, soit une variable
Le résultat retourné est le nombre d’octets nécessaire pour stocker une valeur de ce type
sizeof(char); /* retourne 1 */
sizeof(unEntier); /* retourne 4 si unEntier est déclarer int */
94
Pointeurs
95
Pointeurs
96
Pointeurs
• Un pointeur est une variable dont la valeur est une adresse
• Un pointeur est une variable qui doit être définie en précisant le type de variable pointée, de la
façon suivante :
type * Nom_du_pointeur ;
• Le type de variable pointée peut être aussi bien un type primaire (tel que int, char...) qu'un type
complexe (tel que struct...).
• Déclaration : type * identificateur ;
• ex: int *pointeurSurEntier ;
• PointeurSurEntier est une variable qui peut contenir l’adresse mémoire d’une variable entière
• int unEntier, *pointeur = &unEntier;
97
Pointeurs
Exemple :
int *p;
On dira que :
p est un pointeur sur une variable du type int , ou bien
p peut contenir l’adresse d’une variable du type int
*p est de type int, c’est l’emplacement mémoire pointé par p.
Grâce au symbole '*' le compilateur sait qu'il s'agit d'une variable de type pointeur et non d'une variable
ordinaire, de plus, étant donné que vous précisez (obligatoirement) le type de variable, le compilateur saura
combien de blocs suivent le bloc situé à l'adresse pointée.
98
Pointeurs
A la déclaration d’un pointeur p, il ne pointe a priori sur aucune variable précise : p est un pointeur
non initialisé.
Toute utilisation de p devrait être précédée par une initialisation.
la valeur d.un pointeur est toujours un entier (codé sur 16 bits, 32 bits ou 64 bits).
Pour initialiser un pointeur, le langage C fournit l’opérateur unaire &. Ainsi pour récupérer l’adresse
d’une variable A et la mettre dans le pointeur P ( P pointe vers A) : P=&A;
99
Pointeurs void *
void *
void *
void * void *
100
Pointeurs
Par exemple :
| int A, B, *P; P = &A ;
int a = 2; | A = 10; B = *P ;
char b; | B = 50; *P = 20;
int *p1; | P = &B;
char *p2; |
p1 = &a; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
p2 = &b; | a) int *p , x = 34; *p = x;
|
| b) int x = 17 , *p = x; *p = 17;
*p1 = 10;
*p2 = 'a'; | c) double *q; int x = 17 , *p = &x; q = p;
|
| d) int x, *p; &x = p;
a = (*p1)++; |
e) char mot[10], car = 'A', *pc = &car ; mot = pc;
|
101
Les fonctions
• On appelle fonction un sous-programme qui permet d'effectuer un ensemble d'instructions par simple appel de
• Les fonctions permettent d'exécuter dans plusieurs parties du programme une série d'instructions, cela permet
une simplicité du code et donc une taille de programme minimale. D'autre part, une fonction peut faire appel à
elle-même, on parle alors de fonction récursive (il ne faut pas oublier de mettre une condition de sortie au risque
sinon de ne pas pouvoir arrêter le programme...).
102
Les fonctions
• Organisation d'un programme :
103
Exemple
Type de la valeur
Argument
de retour
106
Type de la fonction
• Une fonction peut ne pas renvoyer de valeur.
• Exemple
void print_majus (char c1) {
char c2;
if (c1 >= 'a' && c1 <= 'z')
c2 = c1+'A'-'a';
else c2=c1;
printf("la majuscule de % est %c, c1, c2);
return; /* ou bien return (); */
}
• Dans ce cas, le type de la fonction est : void
• Le type de la fonction peut être :
• int, float, char, ou adresse …
107
Instruction return
1/ Indique la valeur de retour de la fonction.
2/ Arrête l'exécution de la fonction
char minus_majus (char c1) {
void main () {
float z,t;
z = puiss(10.7,2);
t = puiss (z, -6);
...
}
109
Appel des fonctions
• Un appel de fonction peut se faire comme opérande d'une expression, soit
comme paramètre d'un autre appel de fonction.
• Exemple
int maximum (int x, int y) {
return((x>y)?x:y));
}
void main () {
int v1,v2,v3,m1;
scanf("%d%d%d" , &v1,&v2,&v3);
m1 = maximum(v1,v2);
m1 = maximum(m1,v3);
printf("valeur maximale %d\n", m1);
}
ou bien
m1 =maximum(v1,v2);
printf("valeur maximale %d\n", maximum(m1,v3));
ou bien
printf("valeur maximale %d\n", maximum(maximum(v1,v2),v3));
110
Règles de déclaration et d'appel
• Toute fonction ne peut appeler que des fonctions déclarées avant elle ou elle-même (la fonction
main ne peut pas s'appeler).
... f1 (..) {
...
}
... f2 (...) {
...
}
... f3 (...) {
...
}
void main (...) {
...
}
la fonction main peut appeler f1,f2,f3
la fonction f3 peut appeler f1,f2,f3
la fonction f2 peut appeler f1, f2
la fonction f1 peut appeler f1
111
Déclarations en "avance"
• Règle précédente contraignante
• Solution : Prototype
Dans un fichier ".h" déclarer les prototypes de toutes les fonctions, par
exemple malib.h
Dans le ou les fichiers ".c", insérer la directive
#include "malib.h"
113
Passage des paramètres
• Rappel : les paramètres sont associés aux arguments suivant l'ordre de déclaration.
• En c, cette association se fait par COPIE de la valeur du paramètre dans l'argument. Chaque argument est en
fait une variable locale de la fonction. La fonction travaille sur l'argument.
• Conséquence : Une fonction ne modifie pas les paramètres d'appels
void f (int a){
a=a+1;
}
void main(){
int b;
b=0;
f(b);
printf("%d\n",b); ->0
}
114
Détail
void f (int a){
a=a+1; /*3*/
}
void main(){
int b;
b=0; /*1*/
f(b); /*2*/
printf("%d\n",b); /*4*/
}
b 0 b 0 b 0 b 0 Inchangé
Copie
a 0 a 1
• Rappels :
opérateur & : & variable -> adresse de la variable
opérateur * : * adresse -> valeur qui se trouve à cette adresse
int i;
int * adresse_i; /* déclaration d'une adresse d'entier */
i=0;
adresse_i=&i;
printf("%d\n",i); -> 0;
...
116
Modification des paramètres
void f2 (int * a){ // a est l'adresse, *a est l'entier
*a=*a+1; /*t3 on incrémente le mot d'adresse a*/
}
void main(){
int b;
b=0; /*t1*/
f(&b); /*t2 &b est l'adresse de b */
printf("%d\n",b); /*t4*/ -> 1
}
b 0 728 b 0 &b b 1 *a b 1
Copie
a 728 &b a 728 &b
/*t1*/ /*t2*/ /*t3*/ /*t4*/
118
Passage d'un tableau à une dimension en paramètre
• Puisque la dimension n'est pas utilisée, on peut ne pas la
donner
void printtab (int t1[50]){
int i;
for (i=0;i<50;i++) Syntaxes équivalentes
printf("%d",t1[i]);
}
ou bien
void printtab (int t1[]) {
int i;
for (i=0;i<50;i++)
printf("%d",t1[i]);
}
119
Passage d'un tableau à une dimension en paramètre
Conséquence : on peut donc appeler cette fonction avec tout tableau d'entiers quelle que soit sa dimension. C’est au
programmeur à gérer les débordements de tableau =>donner le nombre de cases sur lequel travaille la fonction
void printtab (int t1[], int n){
int i;
for (i=0;i<n;i++)
printf("%d",t1[i]);
}
void main () {
int t[50],t2[100];
...
printtab(t,50); /*affiche toutes les cases de t de 0 à 49*/
printtab(t2,100); /*affiche toutes les cases de t2 de 0 à 99*/
printtab(t+20,30);/*affiche toutes les cases de t de 20 à 49*/
printtab(t+20,10);/*affiche toutes les cases de t de 20 à 30*/
}
120
Passage d'un tableau à une dimension en paramètre
121
Passage d'un tableau à une dimension en paramètre
Conséquence :
Si un argument est de type tableau (càd une adresse), la fonction
peut modifier les cases du tableau
void misea0 (int t1[], int n){
int i;
for (i=0;i<n;i++)
t1[i]=0;
}
void main () {
int t[10]={1,2,3,4,5,6,7,8,9,10};
printtab(t,10); -> 1 2 3 4 5 6 7 8 9 10
misea0(t,10);
printtab(t,10); -> 0 0 0 0 0 0 0 0 0 0
}
122
Visibilité des variables
ou portée des variables les règles qui régissent l'utilisation des
• On appelle visibilité
variables. Les mêmes règles régissent les types.
void f1 () {
i = i+1;
}
void main(){
i=0;
f1();
printf("%d\n",i) ; //-> 1
}
123
Visibilité des variables
• Règle 2 : variables locales
Les variables déclarées dans une fonction ne peuvent être
utilisées que dans cette fonction. Ces variables sont dites
locales.
void f1 () {
int i;
i = i+1;
}
void main(){
i=0; -> ERREUR : i n'existe pas pour main
...
}
124
Visibilité des variables
• Règle 3 : arguments = variables locales
Les arguments d'une fonction sont des variables locales de la fonction.
void f1 (int i) {
i = i+1; /* i est une variable locale de la fonction */
}
void main(){
int j=1;
f1(j);
printf("%d\n",j)
}
• Règle 4 : Au sein d'une fonction, toutes les variables doivent avoir des noms distincts
void f1 () {
int i;
char i; -> ERREUR : i existe déjà
i = i+1;
}
125
Visibilité des variables
• Règle 5 : Des variables déclarées dans des fonctions différentes peuvent porter le même nom
sans ambiguïté.
void f1 () {
int i; sous-entendu i_f1
...
}
void f2 () {
char i; sous-entendu i_f2
...
}
void main(){
int i; sous-entendu i_main
....
}
Ces 3 variables n'ont rien de commun 126
Visibilité des variables
Si une variable globale et une variable locale ont
• Règle 6 :
le même nom, on accède à la variable locale dans la fonction où
elle est déclarée
int i;
void f1 () {
int i;
i=2; /* i de f1 */
}
void main(){
i=0; /* i global */
f1();
printf (%"d\n",i); -> 0
}
127
Conseils
• Evitez autant que possible l'usage des variables globales => limitation des effets de
bord indésirables
int i;
void f1 () {
...
i=i+1;
}
void main(){
i=0;
f1();
printf (%"d\n",i); -> 1
}
• Dans f1, on travaille sur i global :
• Est-ce bien ce que l'on désirait (oubli de déclaration d'une nouvelle
variable locale ?)
• Débogage difficile : il faut inspecter le code en détail pour voir où sont
modifiées les variables.
128
Conseils
• Si l'on ne peut éviter les variables globales, respecter un
code pour différencier les variables globales des variables
locales.
• Par exemple :
si l'initiale de la variable est une majuscule -> globale : Vglob
minuscule -> locale : vloc
ou bien
le nom de chaque variable globale commence par G_ : G_variable
etc...
• Pas de confusion entre locales et globales.
129
permut1 && permut2
main ()
void permut1 (int a, int b){ {
int c; int u,v;
printf("AVANT: a=%d \t b=%d \n",a,b); u=2;v=3;
c=a;a=b;b=c; permut1(u,v);
printf("APRES: a=%d \t b=%d \n",a,b); printf("Permut1:\t u=%d \t v=%d
} \n",u,v);
void permut2 (int *a, int *b){ /*---------------------------*/
int *c; permut2(&u,&v);
printf("Permut2:\t u=%d \t v=%d
printf("AVANT: a=%d \t b=%d \n",*a,*b);
\n",u,v);
*c=*a;
*a=*b; }
*b=*c;
printf("APRES: a=%d \t b=%d \n",*a,*b);
}
130
permut1 && permut2
131
tableau et pointeur
#include <stdio.h>
main()
{
int v[10],*q;
tmp(v,3);
tmp(&v[1],4);
q=v;
printf("\nv[%d]=%d\n",0,*q);
q++;
printf("\nv[%d]=%d\n",1,*q);
}
132
Const && pointeur
• Les pointeurs peuvent être rendus const. Le compilateur
s'efforcera toujours d'éviter l'allocation, le stockage et
remplacera les expressions constantes par la valeur appropriée
quand il aura affaire à des pointeurs const, mais ces
caractéristiques semblent moins utiles dans ce cas. Plus
important, le compilateur vous signalera si vous essayez de
modifier un pointeur const, ce qui améliore grandement la
sécurité.
133
exemple
int d = 1; int* -Pointeur vers int
int const * -pointeur const int
const int* const x = &d; // (1) int * const - const-pointeur int
int const * const -const-pointeur const int
int const* const x2 = &d; // (2)
134
const
const char *s; // read as "s is a pointer to a char that is constant"
char c;
char *const t = &c; // read as "t is a constant pointer to a “
char *s = 'A'; // Can't do because the char is constant
s++; // Can do because the pointer isn't constant
*t = 'A'; // Can do because the char isn't constant
t++; // Can't do because the pointer is constant
135
Next : Allocation Dynamique de la Mémoire
Allocation de mémoire
Déallocation de mémoire
Tableaux (n dimensions)
136
Allocation de mémoire
Mémoire Statique:
La mémoire est allouée par le linker au début du programme, et est libérée
lorsque le programme à fini d'éxécuter.
Mémoire Automatique:
La mémoire est automatiquement allouée, gérée et libérée pendant
l'éxecution du programme. Les arguments des fonctions et les
variables locales obtiennent de l'espace de cette manière
Mémoire Dynamique:
La mémoire est requise explicitement par le programme(ur). Le
programme(ur) gère et libère la memoire (en principe).
137
Où se trouve la variable?
compile-time program-text STATI Q U E
variables globales
variables static
automatic stack pile AUTO MATI Q U E
variables locales
parametres de fonctions
valeur de retour des fonctions
run-time heap tas DYNAMIQUE
malloc
calloc
Realloc
138
Les espaces mémoire alloués de manière statiques ou automatiques ne sont généralement pas 1
problème pour le programmeur (qui n‘a généralement pas besoin de s'en occuper). Il faut
cependant en être conscient pour pouvoir optimiser ces progrmmes.
AUTOMATIQUE
int x; /* global */
Variables Locales
int f(int n) {
Paramétres de fonctions
int x; /* local to f */ Retours de fonctions
if (n > 3) {
int x; /* local to if */
... }
STATI Q U E
{
Constantes
/* a local scope */
Variables globales
int x;
Variables déclarées: static
}
}
139
Allocation de mémoire dynamique :
140
int * x = (int*)malloc(sizeof(int));
int * a = (int*)calloc(10,sizeof(int));
*x = 3;
a[2] = 5;
free(a);
a = 0;
free(x);
x = 0;
• Valeur retournée :
• Si l'allocation réussit, malloc retourne un pointeur sur le début du bloc
alloué. Si la place disponible est insuffisante ou si size vaut 0, malloc retourne
NULL.
if ( ptr == NULL)
{printf("Allocation mémoire impossible\n"); exit(1);}
/* libération de la mémoire */
free(ptr);
}
143
Demande d'allocation de mémoire (calloc)
o La fonction calloc réserve un bloc de taille nelemxelsize octets consécutifs. Le bloc alloué
est initialisé à 0.
o Syntaxe :
#include <stdlib.h>
void *calloc(size_t nelem, size_t elsize);
o Valeur retournée :
Si succès, calloc retourne un pointeur sur le début du bloc alloué. Si échec, calloc
retourne NULL s'il n'y a plus assez de place ou si nelem ou elsize valent 0.
o Attention : Les fonctions d'allocation dynamique retournent des pointeurs sur des void. Il
faut donc opérer des conversions de types explicites pour utiliser ces zones mémoire en
fonction du type des données qui y seront mémorisées.
144
#include <stdio.h>
#include <stdlib.h>
int main() {
int *str = NULL;
str = (int *) calloc(10, sizeof(int));
printf("%d\n", str[9]);
free(str);
return 0;
}
145
Demande d'allocation de mémoire (realloc)
147
Libération
La fonction free libère un bloc mémoire d'adresse de début ptr.
Syntaxe :
#include <stdlib.h>
void free(void *ptr);
Ce bloc mémoire a été précédemment alloué par une des fonctions malloc, calloc,
ou realloc.
Attention :
Il n'y a pas de vérification de la validité de ptr. Ne pas utiliser le pointeur
ptr après free, puisque la zone n'est plus réservée.
A tout appel de la fonction malloc (ou calloc) doit correspondre un et un seul appel
à la fonction free.
148
#include <stdio.h>
#include <stdlib.h>
int main() {
char *str;
str = (char *) malloc (100 * sizeof (char));
gets(str);
/* saisie d'une chaine de caracteres */
/* suppression des espaces en tete de chaine */
while ( *str == ' ') str++;
/* free ne libere pas toute la zone allouee
car ptr ne designe plus le debut de la zone
memoire allouee par malloc */
free (str);
return 0;
}
149
Déallocation de la mémoire de notre matrice 2 Dimensions:
void free_matrix( double** M, int n) {
int i;
for(i = 0; i<n; ++i)
free (M[i]);
free(M);
}
...
free_matrix(M, 5);
M = 0;
150
Arithmetique des pointeurs:
Il est possible de déplacer la position d'un pointeur en lui ajoutant ou en lui retranchant une
valeur entière. Par exemple:
ptr_str += 1;
• Les entiers ajoutés ou retranchés ont des tailles scalaires différentes selon le type du
pointeur. En d'autres termes, ajouter ou soustraire 1 à un pointeur n'équivaut pas à se
déplacer d'un octet, mais à aller à l'adresse suivante ou précédente.
151
Arithmetique des pointeurs:
152
Arithmetique des pointeurs:
==
*(p[1] + 2) + 3 == *(*(p + 1) + 2) + 3
153
154