0% ont trouvé ce document utile (0 vote)
419 vues154 pages

Langage C 2025

Very god pdf

Transféré par

معلومة stories
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
419 vues154 pages

Langage C 2025

Very god pdf

Transféré par

معلومة stories
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Langage C

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.

• Ce langage est pensé comme un assembleur portable : son pouvoir

d'expression est une projection des fonctions élémentaires d'un


microprocesseur idéalisé et suffisamment simple pour être une abstraction
des architectures réelles.

5
La fonction principal

• La fonction « main » contient le programme principal

• Le programme exécutable binaire commence par exécuter les


instructions de ce programme principal

• Sans la fonction main, il est impossible de générer un programme


exécutable

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

• Un programme est composé de plusieurs fonctions qui échangent et modifient


des variables
• Un processus est l'abstraction d'un programme exécute par la machine.

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

un nom associe a de l'espace mémoire


10
Identificateurs
Exemples :
abc, Abc, ABC sont des identificateurs valides et tous différents.

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)

Et pour les types !!!


11
Types de variables manipulées en C

• Toutes les variables doivent être explicitement typées (pas de déclaration


implicite)

• Il y a globalement trois types de variables :


• Les entiers : int, short int, long int
• Les réels : float, double, long double
• Les caractères : char
• Rien … : void

• exemples : short int mon_salaire;


double cheese;
char avoile;

12
Types de base

4 types de base, les autres types seront dérivés de ceux-ci.

Type Exemples de valeur Codage en Peut être


Signification
mémoire
'a' 'A' 'z' 'Z' '\n' 'a' 'A'
1 octet signed,
char Caractère unique 'z' 'Z' '\n'
unsigned
Varie de –128 à 127
Short, long,
0 1 -1 4589 32000
int Nombre entier 2 ou 4 octets signed,
-231 à 231 +1
Unsigned,
Nombre réel 0.0 1.0 3.14 5.32
float 4 octets
simple -1.23

Nombre réel 0.0 1.0E–10 1.0 -


double 8 octets long
double précision 1.34567896

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

Débute par /* et se termine par */


/* Ceci est un commentaire */
// Ceci est un commentaire

14
Un identificateur ne peut pas être un mot réservé du langage :

auto double int struct


break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while

double, int, long, break, else, switch, struct, enum,


char, const, float, case, return, typedef, union
short, unsigned, continue, for,
4
signed, void, sizeof default, goto, do, if,
while auto, register,
11
12 5
extern, volatile,
static

Les mots réservés du langage C doivent être écrits en minuscules. 15


Un identificateur ne peut pas être un mot réservé du langage :

auto double int struct


break else long switch
case enum register typedef
char extern return union
const float short unsigned
Continue for signed void
default goto sizeof volatile
do if static while

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)

Par exemple, l'affectation foo = 2 provoque :


• l'effet latéral : l'entier 2 est affecte a la variable foo ;
• et retourne la valeur qui vient d‘être affectée.

if (x=1) printf("\n tropical \n");


If (x=0) printf("\n tropical \n");

18
Structure d ’un programme C

• Plusieurs instructions peuvent être rassemblées par des


accolades { } et forme un bloc, par exemple :
if (x !=0)
{
z = y / x;
t = y % x;
}

19
Qu’est-ce qu’un bloc d ’instructions ?

• Un bloc débute par une accolade ouvrante et se termine par une


accolade fermante

• Il contient des déclarations de variables internes au bloc et des


instructions

• Les lignes d’instructions se terminent par des points virgules.

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];

• Toute variable doit être déclarée avant d’être utilisée en C.

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é.

• Ce préprocesseur exécute des instructions particulières appelées directives.

• Ces directives sont identifiées par le caractère # en tête.

• Inclusion de fichiers
#include <nom-de-fichier> /* répertoire standard */
#include "nom-de-fichier" /* répertoire courant */

 La gestion des fichiers (stdio.h) /* Entrees-sorties standard */

 Les fonctions mathématiques (math.h)

 Traitement de chaînes de caractères (string.h)

 Le traitement de caractères (ctype.h)

 Utilitaires généraux (stdlib.h)

 Date et heure (time.h)


23
Directives du préprocesseur

• #define chaine1 chaine2


remplacement littéral de la chaîne de caractères chaine1 par
chaine2

/*
* 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

Expliquer, ligne par ligne, la


signification de tous les
termes de ce programme

Expliquer la différance entre ces deux codes

26
La fonction printf()
Librairie : stdio.h #include <stdio.h>

Syntaxe :int printf( const char *format [, arg [, arg]...]);

Description : Permet l'écriture formatée (l'écran par défaut).

Exemple :
printf("Qu'il est agreable d’utiliser printf en\t C,\nlorsqu'on l'utilise
\"proprement\".\n");

Résultat sur la sortie :


Qu'il est agreable d’utiliser printf en C,
lorsqu'on l'utilise "proprement".

Les caractères précédés de \ sont interprétés comme suit :


\\ : caractère \
\n : retour à la ligne
\t : tabulateur.
\" : caractère "
\a : sonnerie
27
La fonction printf()
Les constantes de type caractère ont une valeur entiére dans la table
ASCII
char c1 = 'A',
c2 = '\x41'; /* représentation hexadécimale */

caractères nom symbol code hexa décimal


\n newline A 10
\t tabulation 9 9
\b backspace 8 8
\\ backslash \ 5C 92
\' single quote ‘ 27 39
\" double quote " 22 34

28
La fonction scanf()
Librairie : stdio.h. #include <stdio.h>

Syntaxe :int scanf( const char *format [, arg [, arg]...]);


Description : Lit à partir de stdin (clavier en principe), les différents
arguments en appliquant le format spécifié.
Exemple : scanf(" %d", &age); /* lecture de l'âge, on donne l'adresse de age */
Ces fonctions utilisent des formats qui permettent de lire/écrire des variables
de différents types : Format des paramètres passés en lecture et écriture.

"%c" : lecture d'un caractère.


"%d" ou "%i" : entier signé.
"%e" : réel avec un exposant.
"%f" : réel sans exposant.
"%g" : réel avec ou sans exposant suivant les besoins.
"%G" : identique à g sauf un E à la place de e.
"%o" : le nombre est écrit en base 8.
"%s" : chaîne de caractère.
"%u" : entier non signé.
"%x" ou "%X" : entier base 16 avec respect majuscule/minuscule.
29
scanf ()
• Sert à la lecture de données et convertit la succession de
caractères donnés en entiers, flottants, caractères, chaîne de
caractères
• Syntaxe :
 scanf (format,arg1,arg2,……,argn)
 le nombre d'arguments est quelconque
 arg1, arg2, …, argn sont les adresses des variables dans lesquelles on
stocke les valeurs lues
 variable simple (entier,caractère,flottant) : &v
 chaîne de caractères = tableau :v
 le format est une chaîne de caractères précisant le type des
arguments afin de convertir la suite de caractères lus dans les
arguments

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);

le %d indique que le premier argument est un entier


le %f indique que le second est un réel
réponse : 2312.6
23 est converti en entier et stocké dans i
12.6 est converti en flottant et stocké dans 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 : ab 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

• Lecture de valeurs numériques : aucun rôle


scanf ("%d%f",&i,&x)  scanf ("%d%f",&i,&x)
• Lecture de caractères : indique de sauter les , tab et 
• Exemples
scanf ("%c%c%c",&c1,&c2,&c3);
rep1 : abc c1= 'a' , c2 = 'b', c3= 'c'
rep2 : abc c1= 'a' , c2 = '', c3= 'b'

scanf ("%c%c%c",&c1,&c2,&c3);
rep1 : abc c1= 'a' , c2 = 'b', c3= 'c'
rep2 : abc c1= 'a' , c2 = 'b', c3= 'c'
rep2 : ab 
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 : 123 i=1 j=2 k=3
rep2 : 123456789 i=123 j=456 k=789
rep3 : 123456789 i=123 j=456 k=789
rep4 :123456789 i=123 j=4 k=567

int i; float x;char c;


scanf("%3d%5f%c,&i,&x,&c);
rep : 10234.567t i=10 x=234.5 c='6'

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()

• Saut conditionnel de caractères : %*d, %*f


permet de sauter des données correspondantes dans la réponse
exemple :
int i,j; char c;
scanf("%d  %*d  %c",&i,&c);
rep1 : 1234x i=12 c='x'
rep2 : 12x i=12 c='x'

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"

raccourcis : [0123456789] ou [0-9]


[abcdefg] ou [a-g]

37
Affichages et saisies
Librairie : stdio.h
Fonction Syntaxe Description

printf printf( const char *format [, arg [, Écriture formatée


arg]...]); : sortie standard

scanf scanf( const char *format [, arg [, Lecture formatée


arg]...]); 7 entrée standard

putchar putchar(int c); Écrire le caractère c


:

getchar getchar(); Lecture d'un caractère


getch(); <conio.h> 7
getch
puts *puts(char *s); Ecriture/Lecture d'une
*gets(char *s); chaîne de caractères,
gets terminée par \n
sprintf sprintf(char *s, char *format, arg ...); Ecrit dans la chaîne
d'adresse s.

sscanf sscanf(char *s, char *format, pointer Lit la chaîne d'adresse s.


...);
38
Exemples :
int main() int main()
{ {
int i; char c1; float c2, c3;
for(i=0;i<=255;i++) scanf("%c%f%f",&c1,&c2,&c3);
printf(" \n %d => %c : %X : %x : printf(" \n %c %f %f ",c1,c2,c3);
%o",i,(char)i,i,i,i); }
}

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

• Dont l'adresse est : *


Syntaxe *expression : donne le mot mémoire dont l'adresse est donnée par
l'expression
Exemple :
int i, ^*adr;
i=1;
adr = &i;
printf("%d", *adr); -> 1

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 ));

int i; scanf("%d",&i); printf("\n %d %X %d %d \n",i,i,~i,&i);


scanf("%d",&i); printf("\n %d %x %d %d \n",i,i,~i,&i);
C // tester avec des nombres positifs et négatifs exemple 2 -2 la representation des chiffres négatif sur un ordinateur 42
Opérateurs et expressions
• Opérateurs arithmétique:
• *,/,+,-
• %modulo
• Opérateurs sur bits:
• <<,>> décalage à gauche ou à droite
• status = byte << 4;
• & et
• | ou
• ^ ou exclusif
Remarque :
7/2 3

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';

f>5 ====> vrai (1)


(i + f) <= 1 ====> faux (0)
c == 'w‘ ====> vrai (1)
c != 'w' ====> faux (0)
(i >= 6) && (c == 'w') ====> vrai (1)
(i >= 6) || (c == 119) ====> vrai (1)

!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;

a = a + 32; f = f / 9.2; i = i * (j + 5);


46
Opérateurs bit à bit
• Opèrent sur les représentations des valeurs
• & et , | ou, ^ ou-exclusif, ~ complément à 1 ,
• << décalage à gauche, >> décalage à droite,
• Attention : &  &&
• Exemples
5 0000 0000 0000 0101
20 0000 0000 0001 0100
5 & 20 0000 0000 0000 0100 => 5 & 20 => 4
5 | 20 0000 0000 0001 0101 => 5 | 20 => 21
5 ^ 20 0000 0000 0001 0001 => 5 ^ 20 => 17
~5 1111 1111 1111 1010 => -6
• Affectation/bit-à-bit : &=, |=, ^=, ~=

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)

• -> et . (opérateurs sur structures, + tard)

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);

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&&31||!2, 100^30 );

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 :

result = (mode > 0) ? 1 : 0;

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

Si expression_test est vraie on exécute le bloc d ’instructions 1, sinon on passe à la suite.

if (expression_test)
bloc_instructions_1
else
bloc_instructions_2

Si expression_test est vraie on exécute le bloc d ’instructions 1 sinon on exécute le bloc 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.

if ((!recu) && (i < 10) && (n!=0) ){


i++; moy = som/n;
printf(" la valeur de i =%d et moy=%f\n", i,moy) ;
}
else {
printf ("erreur \n"); i = i +2; // i +=2 ;
}

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);
}

Incrémentation et autres fonctions

64
Exemples
double angle;

for(angle = 0.0; angle < 3.14159; angle += 0.2)


printf("sine of %.1lf is %.2lf\n",angle, sin(angle));

int i, j, k;

for(i = 0, j = 2, k = -1; (i < 20) &&(j==2); i++, 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;

case ‘b’ : fonctionB(); break; Exécuté si choix = c

case ‘c’ : fonctionC(); break;


Exécuté si choix non
default : erreur(3); répertorié par un « case »
}
}
71
Instructions d'échappement
Pour rompre le déroulement séquentiel d'une suite d'instructions

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

• Introduire la notion de pointeur

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)

tab peut recevoir 100 entiers identifiés de 0 à 99 (attention !)


le premier est tab[0]
le second tab[1]
..
le dernier tab [99]

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;

2/ le nombre maximum d'éléments du tableau (dimension)


1/ doit être fixé à la compilation
2/ ne peut être modifié pendant l'exécution
3/ Pas d'opérations sur les tableaux en tant que tels

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]= …….;

Parcours du dernier au premier


int i; /* l’indice de balayge doit être un entier */
float t[100]; /* le type du tableau est quelconque */
for (i=99; i >= 0; 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 ….

#define TAILLE 100


int i;
int t[TAILLE];
for (i=0; i < TAILLE; i=i+1)
t[i]= 100;

Il suffit de changer TAILLE

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

Mémorisation des 100 premiers nombres pairs et impairs:

int pairs[100], impairs[100];


int i;
for (i=0; i<100;i=i+1) {
pairs[i]=2*i;
impairs[i]=2*i+1;
}

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";

• Constante chaîne de caractères


#define ceciestuntexte "azssddqsdqsd"
82
Définitions de type utilisateur
typedef int entier; (entier est maintenant un type)
entier i;

typedef int tableaude20[20];


tableaude20 t; // équivalant int t[20];

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)

Déclaration et implantation mémoire :

int t[50]; => réservation dans la mémoire de 50 cases contiguës d'entiers.

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 ? ? ? ? ? ? ?

• Dimension par défaut:


int t[ ]={0,0,0} => dimension =3
char t[ ]={'r','v','b','j'}; => dimension=4
char t[ ]="abcd" => dimension=5

par contre int t[ ] sans initialisation est incorrect

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"

• Accès aux éléments


par un 1er indice allant de 0 à 19 et par un 2eme indice allant de 0 à 99
matrice[3] est la 4eme case de tableau. C'est un tableau de 100 cases (entiers)
matrice[3][48] est un entier.
matrice [i][j] avec i de 0 à 19 et j de 0 à 99
matrice est un tableau à 2 dimensions

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];

tab[2][5][1] représente le "y" du point rangé en ligne 2 et colonne 5

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}};

int t[3][2] = {{1,2},4,{5,6}}; => t[1][1] non initialisé

• Initialisation grâce à des boucles


for (i=0;i<3;i++)
for (j=0;j<2;j++)
t[i][j]=0;

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)
{

void main() int t, i, num[3][4];


{
for(t = 0; t < 3; ++t)
char t[3][3] = {
for(i = 0; i < 4; ++i)
{'1','2','3'},
num[ t ][ i ] = ( t * 4 ) + i + 1;
{'4','5','6'},
{'7','8','9'} /* now print them out */
}; for(t = 0; t < 3; ++t) {
printf("value of t[0][0] : %c\n", t[0][0]);
printf("value of t[0] : %c\n", *t[0]); for(i = 0; i < 4; ++i)
printf("value of t : %c\n", **t); printf("%3d ", num[ t ][ i ]);
}
printf("\n");
}
return 0;
}
91
Adresse
comprendre ce qu'est une adresse.

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,

et la taille de cette zone correspond au type de variable que l'on a déclaré.

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

identifiée par un numéro. Ce numéro s'appelle adresse.

92
Adresse

On utilise très souvent les adresses dans :


la saisie des données avec scanf

les appels des fonctions pour recevoir des valeurs retournées

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

Intérêt des pointeurs


On utilise des pointeurs dans :
La transmission des arguments par pointeurs (résultats de retour)
Permettre le passage par Adresse pour des paramètres des fonctions

La manipulation des tableaux


Les structures de données dynamiques (tableaux, liste linéaire chaînée, arbre binaire, ...)
Réaliser des structures de données récursives (listes et arbres)

95
Pointeurs

Un pointeur est une variable contenant l'adresse d'une autre variable


d'un type donné.
La notion de pointeur est une technique de programmation très
puissante, permettant de définir des structures dynamiques, c'est-à-dire
qui évoluent au cours du temps (par opposition aux tableaux par
exemple qui sont des structures de données statiques, dont la taille est
figée à la définition).

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 *

Un pointeur doit préférentiellement être typé !


Il est toutefois possible de définir un pointeur sur 'void', c'est-à-dire sur quelque
chose qui n'a pas de type prédéfini (void * toto).
Ce genre de pointeur sert généralement de pointeur de transition, dans une
fonction générique, avant un transtypage permettant d'accéder effectivement aux
données pointées.

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

la fonction dans le corps du programme principal.

• 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...).

• Une fonction permet de :

• Remplacer une partie qui se répète

• Découper un programme en parties isolées -> débogage, lisibilité, etc..

• Exemples : fonctions d'E/S (scanf, printf, …), mathématiques (sin, cos, …)

102
Les fonctions
• Organisation d'un programme :

type fonction1 (arguments) {


Déclarations de variables et de types locaux à la fonction
Instructions
}
type fonction2 (arguments) {
Déclarations de variables et de types locaux à la fonction
Instructions
}
...
void main (arguments) {
Déclarations de variables et de types locaux à la fonction
Instructions
}

103
Exemple
Type de la valeur
Argument
de retour

char minus_majus (char c1) {


char c2; /* déclarations locales */
if (c1 >= 'a' && c1 <= 'z')
c2 = c1+'A'-'a';
else c2=c1; Instructions
return (c2);
}
void main() {
char c,majuscule;
printf("Donner un caractere\n");
c = getchar(); getchar();
majuscule = minus_majus(c);
printf ("La majuscule de %c est %c\n",c,majuscule); Appel de la fonction
}
104
105
Définition de fonction : syntaxe
type_fonction nom_fonction (type_arg1 arg1, …, type_argn argn) {

return (valeur retournée);
}

Dans l'exemple précédent :


type_fonction : char, c'est le type de la valeur renvoyée par return
nom_fonction : minus_majus

Le nombre d'arguments est quelconque, éventuellement aucun, les parenthèses


doivent toujours figurer (ex: main () )

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) {

if (c1 >= 'a' && c1 <= 'z')


return (c1+'A'-'a');
else return (c1);
}
Pour les fonction de type void, return est optionnel
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);
} 108
Appel des fonctions
• L'appel d'une fonction se fait en donnant son nom, suivi de la liste des paramètres
entre parenthèses. L'ordre des paramètres correspond à celui des arguments.
• Exemple
float puiss (float x, int n) {
float y=1.0;
if (n>0) for (i=1;i<=n;i++) y = y*x;
else for (i=1;i<= - n;i++) y = y/x;
return (y);
}

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

• Lorsqu'une fonction s'appelle elle-même, on dit qu'elle est "récursive".

111
Déclarations en "avance"
• Règle précédente contraignante

• Solution : Prototype

En début de programme on donne le type de chaque fonction , son nom, le


nombre et les types des arguments
• Information suffisante pour le compilateur.
float puiss (float,int);
/*Prototype ou declaration de la fonction puiss*/
void main(){
puiss (10.2, 5); /*Appel avant definition*/
...}
float puiss (float x, int n){ /*la definition de la fonction */
float y=1.0;
if (n>0) for (i=1;i<=n;i++) y = y*x;
else for (i=1;i<=n;i++) y = y/x;
return (y);
}
112
Fichier "header"
• Conseil de programmation :

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

/*1*/ /*2*/ /*3*/ /*4*/ 115


Modification des paramètres
• Si l'on veut qu'une fonction modifie un paramètre, on ne passe pas la variable mais
l'adresse de la variable. Il y a copie de l'adresse de la variable. Dans la fonction on va
chercher la variable par son adresse.

• 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*/

Exemple : scanf ("%d",&v);


117
Passage d'un tableau à une dimension en paramètre
• Rappels:
• Lorsqu'on déclare un tableau, par ex int t[10], t est l'adresse du 1er
élément du tableau
• Chaque élément du tableau peut être accédé par t[i] ou *(t+i)
• La première dimension d'un tableau n'est pas utilisée dans le calcul de
l'adresse d'une case
• Exemple
void printtab (int t1[50]) {
int i;
for (i=0;i<50;i++)
printf("%d",t1[i]);
}
void main () {
int t[50];
.....
printtab(t);
}

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

Puisqu'en fait t1 est une adresse, on peut le déclarer comme tel

void printtab (int * t1,int n) {


int i;
for (i=0;i<n;i++)
printf("%d",t1[i]);
}

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.

• Règle 1 : variables globales


Les variables déclarées avant la 1ere fonction peuvent être utilisées dans toutes les
fonctions. Ces variables sont dites globales.
int i;

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

void permut1 (const int a, const int b){


main ()
int c;
{
printf("AVANT: a=%d \t b=%d \n",a,b); int u,v;
c=a;a=b;b=c; u=2;v=3;
printf("APRES: a=%d \t b=%d \n",a,b); permut1(u,v);
} printf("Permut1:\t u=%d \t v=%d
void permut2 (const int *a, const int *b){ \n",u,v);
int *c; /*---------------------------*/
printf("AVANT: a=%d \t b=%d \n",*a,*b); permut2(&u,&v);
*c=*a; printf("Permut2:\t u=%d \t v=%d
*a=*b; \n",u,v);
*b=*c; }
printf("APRES: a=%d \t b=%d \n",*a,*b);
}

131
tableau et pointeur
#include <stdio.h>

void tmp( int *q, int val);


void tmp( int *q, int val)
{
*q=val;
}

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)

Maintenant la première const peut être de chaque côté du


type :
•const int *==int const *
•const int * const==int const * const

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)

 Arithmetique des pointeurs

136
Allocation de mémoire

Dans C comme dans C++, il y à 3 manières d’allouer de l’espace 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 :

 L'allocation de mémoire dynamique a par contre tendance à être 1 peu +


problématique pour le programmeur. C lui qui l'alloue, qui la gère et qui n'oubli pas de
la rendre au systeme quand il n'en a + besoin. Si la job est mal faite, attendez vous à
des problèmes!!!

 Le heap sert à l'allocation dynamique de blocs de mémoire de taille variable.

 De nombreuses structures de données emploient tout naturellement l'allocation de


mémoire dans le heap, comme par exemple les arbres et les listes.

 Le seul risque est la fragmentation du heap, par allocation et libération successives.


Il n'existe pas en C de mécanisme de "ramasse-miettes" (garbage collector).

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;

Fonctions de gestion de mémoire (C)


void* malloc(size_t size);
void* calloc(size_t n, size_t size);
void* realloc(void * ptr,size_t size);
void free(void * ptr);
141
Demande d'allocation de mémoire (malloc)
malloc demande l'allocation d'un bloc de mémoire de size octets consécutifs dans la
zone de mémoire du heap.
Syntaxe :
#include <stdlib.h>
void *malloc(size_t size);

• 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.

• 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.
142
#include <stdio.h>
#include <stdlib.h>
main() {
char *ptr;
ptr = (char *) malloc(80);
/* demande d'allocation de 80 octets */

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)

La fonction realloc ajuste la taille d'un bloc à size octets consécutifs.


Syntaxe :
#include <stdlib.h>
void *realloc(void *ptr, size_t size);
Valeur retournée :
Si succès, retourne l'adresse de début du bloc réalloué. Cette adresse peut
avoir changé par rapport à celle fournie en argument. Dans ce cas, le
contenu de l'ancien bloc est copié à la nouvelle adresse et l'ancienne zone
est automatiquement libérée. Si échec, (pas assez de place en mémoire ou
size à 0), realloc retourne la valeur NULL
Arguments :
ptr : pointeur sur le début d'un bloc mémoire créé par malloc, calloc, ou realloc.
Si ce pointeur est NULL, realloc équivaut à malloc. size : nouvelle taille du
bloc en octets.
146
2 dimensions (matrices):
double ** alloc_matrix(int n, int m) {
double ** M =(double**)calloc(n,sizeof(double*));
int i;
for(i=0; i<n; ++i)
M[i] = (double*)calloc(m,sizeof(double));
return M;
}

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;

Le compilateur avance d'un ou plusieurs octets par rapport à la position de ptr_str.

• 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:

Le format général du déplacement d'un pointeur est le suivant:


pointeur + n
n est un entier positif ou négatif. pointeur est le nom de la variable déclarée ainsi:
type* pointeur;
Le compilateur interprète l'expression pointeur + n comme suit:
pointeur + n * sizeof(type)

152
Arithmetique des pointeurs:

p[1][2] + 3 == (*(p + 1))[2] + 3

==

*(p[1] + 2) + 3 == *(*(p + 1) + 2) + 3

153
154

Vous aimerez peut-être aussi