Introduction ` a la programmation en C
Cours 1 Premiers pas en C
12 f´ evrier 2014
Avant de commencer
page web du cours :
http://monge.univ-mlv.fr/~pivoteau/INTROC/index.html + Moodle
B transparents de cours, sujets de TD
B sujets de TP, programmes, quelques corrections B bibliographie et lien utiles
3 s´ eances de cours 5 s´ eances de TD 6 s´ eances de TP
contrˆ ole des connaissances : B contrˆ ole continu : TPs B exam
B TP not´ e/micro-projet ?
But du cours
Intro ` a la programmation en C
B comparaison avec un autre langage imp´ eratif (Python) Familiarisation avec la syntaxe
Questions autour de la gestion de la m´ emoire Pr´ eparation au cours de 2e ann´ ee
B Tout ce qui est vu en TD (fini ou non) doit ˆ etre su.
B Tous les TPs sont ` a finir et ` a rendre.
Syntaxe et ´ el´ ements du
langage C
Pr´ esentation du langage C
Kernighan et Ritchie 1972
Langage tr` es r´ epandu, imp´ eratif, non objet, utilis´ e pour la programmation syst` eme
Langage compil´ e, compilateur gcc Peu de librairies
Langage bas-niveau, proche du syst` eme, gestion manuelle de la m´ emoire, pas de garbage collector
Langage typ´ e (` a la diff´ erence de Python)
Syntaxe similaire en Java
Structure d’un programme C
Programme helloWorld.c :
# i n c l u d e < s t d i o . h >
/* f o n c t i o n p r i n c i p a l e */
int m a i n (int argc , c h a r * a r g v [ ] ) {
p r i n t f (" H e l l o w o r l d \ n "); // a f f i c h a g e r e t u r n 0;
}Remarques :
points-virgules, blocs fonction main
pas de top-level
types
Syntaxe : variables et types
Toutes les variables ont un type !
t y p e n a m e ; // d ´e c l a r a t i o n n a m e = v a l u e ; // a f f e c t a t i o n
t y p e n a m e = v a l u e ; // i n i t i a l i s a t i o n
Types primitifs en C :
int entier (relatif) court 2 octets
long entier long 4 octets
float flottant court 4 octets double flottant long 8 octets
char caract` ere 1 octet
unsigned modificateur
pas de bool´ eens : 0 est faux et tout le reste est vrai !
Variables et types : exemples
int i = 0;
i = 3;
i ++; // i n c r ´e m e n t a t i o n ++ i ; // i n c r ´e m e n t a t i o n d o u b l e z = i * 1 . 0 ;
f l o a t g , h ; // d ´e c l a r a t i o n s m u l t i p l e s g = i /3; // d i v i s i o n e n t i `e r e
h = z /3; // d i v i s i o n r ´e e l l e c h a r c =’ s ’;
c - -; // les c a r a c t `e r e s s o n t des e n t i e r s c + = 5 ;
u n s i g n e d int x = -1; // et p o u r t a n t , c ’ est p o s i t i f ...
Syntaxe : op´ erateurs et priorit´ es
Cat´ egorie d’op´ erateurs Op´ erateurs Assoc.
( ) [ ] . −> G→D unaires − ++ −− ! ˜ ∗ & sizeof (type) D→G
mult., div., mod. * / % G→D
addition, soustraction + − G→D
binaires de d´ ecalage << >> G→D
relationnels < <= > >= G→D
de comparaison == ! = G→D
et binaire & G→D
ou exclusif binaire ˆ G→D
ou binaire | G→D
et logique && G→D
ou logique || G→D
conditionnel ?: D→G
affectation = + = − = ∗ = etc... D→G
virgule , G→D
Affichage (format)
#include <stdio.h>
int i = 0;
p r i n t f (" % d \ n ", i );
i = 3;
p r i n t f (" % d \ n ", i );
i ++;
p r i n t f (" % d \ n ", i );
++ i ;
p r i n t f (" % d \ n ", i );
p r i n t f (" % d \ n ", i + + ) ; p r i n t f (" % d \ n ",++ i );
d o u b l e z = i * 1 . 0 ;
p r i n t f (" % d % f \ n ",z , z );
f l o a t g , h ; g = i /3;
h = z /3;
p r i n t f (" % f % f \ n ",g , h );
c h a r c =’ s ’;
p r i n t f (" % d \ n ", c );
p r i n t f (" % c \ n ", c );
c - -;
p r i n t f (" % c \ n ", c );
c + = 5 ;
p r i n t f (" % c \ n ", c );
Page de manuel : man 3 printf
p r i n t f (" % s , %.2 f , *% -4 d * , *%4 d *\ n ", " t o t o : ", 1/3. , 2 , 1);
Saisie au clavier
La fonction scanf permet de choisir le type des donn´ ees saisies grˆ ace ` a un format (comme printf).
Attention, scanf utilise les adresses des variables pour stocker le r´ esultat !
# i n c l u d e < s t d i o . h >
c h a r c ;
int i = 0;
s c a n f (" % c ",& c );
s c a n f (" % d ",& i );
p r i n t f (" *** % d ,% c * * * \ n ",i , c );
Conditions : exemple
Exemple :
u n s i g n e d int x = -1;
int y = -1;
if ( x < 0) if ( y < 0){
p r i n t f (" % d n e g a t i f \ n ", x );
p r i n t f (" % d n e g a t i f \ n ", y );
} e l s e
p r i n t f (" % d n e g a t i f \ n % d p o s i t i f \ n ", x , y );
if ( x > 0 && y < 0)
p r i n t f (" % d p o s i t i f \ n % d n e g a t i f \ n ", x , y );
e l s e{
p r i n t f (" % d p o s i t i f \ n ", x );
p r i n t f (" % d p o s i t i f \ n ", y );
}Est-ce correct ? Qu’est-ce que ¸ ca affiche ?
Conditions
if ( < c o n d i t i o n >) // ne pas o u b l i e r les p a r e n t h `e s e s !
< s t a t e m e n t 1 >
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / if ( < c o n d i t i o n >)
< s t a t e m e n t 1 >
e l s e
< s t a t e m e n t 2 >
Les instructions ( <statement> ) peuvent ˆ etre remplac´ ees par des blocs :
{
< o p t i o n a l - d e c l a r a t i o n - list >
< o p t i o n a l - s t a t e m e n t - list >
}Conditions : variantes
if ( a > b ) r e s u l t = x ; e l s e
r e s u l t = y ; // ´e q u i v a u t `a :
r e s u l t = a > b ? x : y ; // if t e r n a i r e
s w i t c h ( < e x p r e s s i o n >){
c a s e < label1 > :
< s t a t e m e n t s 1 > // a t t e n t i o n `a la t r a p p e ! c a s e < label2 > :
< s t a t e m e n t s 2 >
b r e a k;
d e f a u l t :
< s t a t e m e n t s 3 >
}Switch : exemple
int t o t o = . . . ; s w i t c h ( t o t o ){
c a s e 0:
p r i n t f (" 0\ n ");
c a s e 1 :
p r i n t f (" 1\ n ");
b r e a k; c a s e 2 :
p r i n t f (" 2\ n ");
d e f a u l t :
p r i n t f (" ni 0 , ni 1 , ni 2\ n ");
}Qu’affiche-t-on si toto vaut 0, 1, 2 ou 3 ?
Boucles
2 types de boucles while:
w h i l e( < c o n d i t i o n > ){
< s t a t e m e n t >;
}do{
< s t a t e m e n t >;
}w h i l e( < c o n d i t i o n > );
B break sort de la boucle (seulement la plus imbriqu´ ee).
B continue recommence la boucle depuis le d´ ebut du bloc (pour un for, l’instruction d’incr´ ement est ex´ ecut´ ee).
Mais quelle est la diff´ erence ?
int i = 0 ;
w h i l e ( i < 1 0 ) { p r i n t f (" % d ", i );
i ++;
}
p r i n t f (" % d \ n ", i );
int i = 0 ;
do{
p r i n t f (" % d ", i );
i ++;
}w h i l e ( i < 1 0 ) ; p r i n t f (" % d \ n ", i );
Boucles – suite
Si vous avez besoin de r´ ealiser les instructions de la boucle au moins une fois, et avant le test...
int k ;
w h i l e( k <0 || k > = 1 0 ) { p r i n t f (" k ? ");
s c a n f (" % d ", & k );
}int j ; do {
p r i n t f (" j ? ");
s c a n f (" % d ", & j );
} w h i l e( j <0 || j > = 1 0 ) ;
Les instructions ne peuvent ˆ etre effectu´ ees que si le test est vrai
p r i n t f (" E n t r e z une v a l e u r > 0\ n ");
s c a n f (" % i ",& i );
w h i l e ( i > 0){
p r i n t f (" 9/% d =% d \ n ",i ,9/ i );
i - -;
}do{
p r i n t f (" 9/% d =% d \ n ",i ,9/ i );
i - -;
}w h i l e ( i > 0);
Boucles – suite
Attention, la boucle for de C n’est pas un for each comme en Python. La fonction range n’existe pas non plus.
Syntaxe :
for ( < init >; < c o n d i t i o n >; < i n c r e m e n t > ){
< s t a t e m e n t >;
}init est execut´ e en premier, une seule fois.
Tant que la condition est vraie, le programme rentre dans le corps de la boucle.
Apr` es l’execution du corps de la boucle, la partie increment est execut´ ee, avant de refaire le test.
Exemple :
for (int i = 0; i < 10; i + + ) { p r i n t f (" % d ", i );
}Boucles – suite
Autres exemples :
for ( c h a r c = ’ z ’; c >= ’ a ’; c - -){
p r i n t f (" c o d e a s c i i de % c = % d \ n ",c , c );
}int cpt = 1 0 ;
for (int i = 1; cpt !=0 && i < I N T _ M A X ; i * = 2 ) { p r i n t f (" % d ", i );
cpt - -;
}for ( i =0 ; i != 10 ; i = r a n d ( ) % 2 0 ) ) { p r i n t f (" % d ", i );
}Bof :
for ( s c a n f (" % d ",& i ); i != 0; i - -){
p r i n t f (" % d ",i - = 2 ) ;
}Fonctions
Attention aux types !
v o i d h e l l o (){
p r i n t f (" h e l l o ");
}
v o i d h e l l o s (int n ){
w h i l e ( n ! = 0 ) { p r i n t f (" h e l l o ");
n - -; // ok , car les p a r a m `e t r e s s o n t p a s s ´e s par v a l e u r . }
}
int p1 (int a , int b ){
int res =1;
for (int i = 0; i < b ; ++ i ) res *= a ;
r e t u r n res ; }
int p2 (int a , int b ){
r e t u r n b == 0 ? 1 : a * p2 ( a , b - 1 ) ;
}Structures de donn´ ees
standard
Structures de donn´ ees
´
enum´ eration
type structur´ e (pour faire des tuples) tableaux automatiques
matrices
chaines de caract` eres (tableaux de caract` eres avec marqueur de fin)
mais pas de listes ni de dictionnaires !
Type ´ enum´ er´ e, type structur´ e
e n u m c a r d _ c o l o r { CLUB ,
DIAMOND , HEART , S P A D E };
s t r u c t c a r d {
e n u m c a r d _ c o l o r c o l o r ; int v a l u e ;
};
int m a i n (int argc , c h a r * a r g v [ ] ) { e n u m c a r d _ c o l o r t r u m p = S P A D E ; p r i n t f (" % d \ n ", t r u m p );
s t r u c t c a r d m y _ c a r d = { trump , 1 0 } ;
p r i n t f (" % d ,% d \ n ", m y _ c a r d . color , m y _ c a r d . v a l u e );
r e t u r n 0;
}
Tableaux automatiques
B Un “objet” automatique est un objet dont l’emplacement en m´ emoire est r´ eserv´ e lors de la compilation (et pas ` a l’ex´ ecution).
B AVANT (C Ansi), la taille d’un tableau automatique devait ˆ etre connue lors de la compilation ; c’´ etait :
soit une constante enti` ere : 4, 18, 150, ...
soit une constante symbolique : #define N 1000
B MAINTENANT (C 99), on a les variable length arrays. La taille n’a plus besoin d’ˆ etre connue ` a la compilation !
On peut ´ ecrire char tab[n], o` u n est un entier.
Mais on ne peut toujours pas d´ eclarer : int tab[];
B Le type des ´ el´ ements du tableau doit ˆ etre sp´ ecifi´ e lors de la
d´ eclaration : type nom tableau[taille];
D´ eclarer et initialiser un tableau (automatique)
# d e f i n e T A I L L E 100 ...
int i ; int n = 1 0 ;
int t a b 0 [ n ]; /* !! ok en C 99 , m a i s pas en C A n s i */
int t a b 1 [ T A I L L E ];
c h a r t a b 2 [ 2 0 ] ;
s h o r t t a b 3 []={0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9}; /* t a i l l e = 10 */
d o u b l e t a b 4 [ ] = { 1 2 . 2 , 3 . 0 , 8 . 1 , 4 . 7 7 7 } ; /* t a i l l e = 4 */
c h a r mot []=" T o t o "; /* { ’ T ’ , ’ o ’ , ’ t ’ , ’ o ’ , ’\0 ’} */
for( i =0; i <= TAILLE -1; i ++) // a t t e n t i o n aux b o r n e s ! t a b 1 [ i ]= i * n ;
for( i =0; i < 2 0 ; i ++) // a t t e n t i o n aux b o r n e s ! t a b 2 [ i ]=’ a ’+ i ;
Attention, pour l’initialisation, il n’existe pas de m´ ecanisme de
Parcourir un tableau
En C, tous les tableaux commencent ` a l’indice 0.
On acc´ ede aux cases d’indice 0 ` a n − 1 (tab. de longueur n).
Une tentative d’acc` es ` a une case d’indice ≥ n provoquera un r´ esultat al´ eatoire et risque en particulier de provoquer une segmentation fault.
for( i =0; i < T A I L L E ; i ++) p r i n t f (" % d ", t a b 1 [ i ]);
for( i = TAILLE -1; i > = 0 ; i - -) p r i n t f (" % d ", t a b 1 [ i ]);
Le cas d’une chaˆıne de caract` eres :
c’est un tableau de caract` eres char[] dont le dernier est obligatoirement ’\0’.
soit on connaˆıt sa taille et on peut proc´ eder de mˆ eme ; soit on utilise la sentinelle ’\0’ pour stopper le parcours.
for( i =0; mot [ i ] ! = \ 0 ; i ++) p r i n t f (" % c ", mot [ i ]);
Tableaux et fonctions
Attention : TABLEAU = POINTEUR ! !
la variable nom tableau contient l’adresse de la premi` ere case du tableau.
B Cons´ equence : un tableau pass´ e en param` etre d’une fonction peut ˆ etre modifi´ e ! !
MAIS : une fonction ne peut pas renvoyer un tableau automatique cr´ e´ e dans cette fonction.
Pourquoi ? Parce que l’espace r´ eserv´ e (allou´ e ) au tableau dans la fonction est d´ etruit ` a la sortie de la fonction.
Par contre, on peut passer en param` etre de la fonction un
tableau d´ ej` a d´ eclar´ e et le modifier.
Exemple
v o i d a f f i c h e T a b (int tab [] , int l g T a b ){
int i ;
for( i =0; i < l g T a b ; i ++) p r i n t f (" % d ", tab [ i ]);
p r i n t f (" \ n ");
}
v o i d m o d i f T a b (int tab []} , int i , int e l e m ){
tab [ i ]= e l e m ; }
int m a i n (int argc , c h a r * a r g v [ ] ) {
int tab []={0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9}; /* t a i l l e = 10 */
a f f i c h e T a b ( tab , 1 0 ) ; /* 0 1 2 3 4 5 6 7 8 9 */
m o d i f T a b ( tab ,5 , -1);
a f f i c h e T a b ( tab , 1 0 ) ; /* 0 1 2 3 4 -1 6 7 8 9 */
r e t u r n 0;
}D´ eclarer et initialiser une matrice
Matrice = Tableau ` a 2 dimensions.
Matrice = Tableau dont les cases sont des tableaux.
B comme pour un tableau classique, on indique sa taille lors de la d´ eclaration :
int m a t r i c e [ 2 ] [ 3 ] ; // ou c h a r m a t r i c e [ n ][ m ];
B comme pour un tableau classique, on peut l’initialiser lors de la d´ eclaration ...
int m a t r i c e [ 2 ] [ 3 ] = {{1 , 2 , 3} , {4 , 5 , 6 } } ; // ou int m a t r i c e [ ] [ 3 ] = {{1 , 2 , 3} , {4 , 5 , 6 } } ;
B ... ou apr` es
m a t r i c e [ 0 ] [ 0 ] = 1;
m a t r i c e [ 0 ] [ 1 ] = 2;
m a t r i c e [ 0 ] [ 2 ] = 3;
m a t r i c e [ 1 ] [ 0 ] = 4; // ...
Parcourir une matrice
int i , j ;
int m a t r i c e [ 3 ] [ 5 ] ; for( i =0; i <3; i ++)
for( j =0; j <5; j ++) m a t r i c e [ i ][ j ]= i + j ;
0 1 2 3 4 1 2 3 4 5 2 3 4 5 6
0 1 2 1 2 3 2 3 4 3 4 5 4 5 6
ou ?
B Convention :
type nomMatrice[nb lignes][nb colonnes];
B matrice[i][j] → case sur la i
eligne, dans la j
ecolonne .
Compl´ ements
Chaˆınes de caract` eres
Attention, toute chaˆıne de caract` eres est un tableau de caract` eres mais la r´ eciproque n’est pas vraie !
Ce qui caract´ erise une chaˆıne de caract` ere est l’\0 final.
Les chaˆınes (d´ eclar´ ees avec la notation []) sont mutables.
Il existe un certain nombre de fonctions pr´ ed´ efinies sur les chaˆınes dans les biblioth` eques standard stdlib.h et string.h :
strlen qui prend un chaˆıne et renvoie sa taille (sans compter l’\0).
atoi et atof qui transforme une chaine en entier/flottant.
...
Lecture au clavier : il faut choisir sa taille ` a l’avance !
c h a r s t r i n g [ 1 0 0 ] ; s c a n f (" % s ", s t r i n g );
p r i n t f (" % s \ n ", s t r i n g );
// p l u s s^ur : c h a r s t r i n g [ 1 0 0 ] ;
f g e t s ( string , 100 , s t d i n );
Qu’est-ce qu’un char[][] ?
Struct 32/35
Chaˆınes de caract` eres - Exemple
int d (c h a r s [] , c h a r t [ ] ) { int a = s t r l e n ( s );
for (int i = 0; i < a ; i ++) if ( s [ i ]!= t [ i ])
r e t u r n 0;
r e t u r n 1;
}
v o i d f (c h a r s [ ] ) { int a = s t r l e n ( s );
c h a r t ;
for(int i =0; i < a /2; i + + ) { t = s [ i ];
s [ i ]= s [ a - i - 1 ] ; s [ a - i - 1 ] = t ; }
}int g (c h a r s [ ] ) { int a = s t r l e n ( s );
c h a r t [ a + 1 ] ;
for(int i =0; i < a ; i ++) t [ i ]= s [ i ];
t [ a ]=’ \0 ’;
f ( t );
r e t u r n d ( s , t );
}
int h (c h a r s [ ] ) { int a = s t r l e n ( s );
for(int i =0; i < a /2; i + + ) { if ( s [ i ]!= s [ a - i - 1 ] )
r e t u r n 0;
}
r e t u r n 1;
}Compilation - Ex´ ecution - Arguments
fichier my prog.c :
# i n c l u d e < s t d i o . h >
# i n c l u d e < s t d l i b . h >
# i n c l u d e " m y _ l i b . h "
# d e f i n e R M A X 2
int m a i n (int argc , c h a r * a r g v [ ] ) { p r i n t f (" p r o g : % s \ n ", a r g v [ 0 ] ) ; if( a r g c = = 3 ) {
int nb = a t o i ( a r g v [ 1 ] ) ;
for (int i = 0; i < nb ; ++ i ) p r i n t f (" % d ", m y R a n d ( R M A X ));
}
e l s e p r i n t f (" b e u h . . . \ n ");
r e t u r n 0;
}fichier my lib.h :
int m y R a n d (int n );
fichier my lib.c :
# i n c l u d e < s t d l i b . h >
int m y R a n d (int n ){
r e t u r n r a n d o m ()% n ;
}pour compiler :
gcc my lib.c my prog.c pour executer :
./a.out ou :
gcc my lib.c my prog.c -o my prog
./my prog 20
Contrˆ ole d’erreur
Pas d’exception, donc pas de m´ ecanisme try ... catch.
Il faut tester avant d’utiliser une fonction qui peut ”rater”.
Il faut tester les valeurs de retour des fonctions qui peuvent rencontrer des probl` emes.
Exemple :
int i ;
int try = s c a n f (" % d ",& i );
if( try ==0 || try == EOF )
p r i n t f (" on d o i t l i r e un e n t i e r !\ n ");
e l s e
if ( i ! = 0 )
p r i n t f (" % d \ n ", 4/ i );
e l s e p r i n t f (" i d o i t ^e t r e non nul !\ n ");
r e t u r n 0;