Accueil
Professeur et Démonstrateurs
Horaires et locaux
Notes de cours
Démonstrations et devoirs
Examens
Liens utiles
Au sujet du livre de cours
Consultez vos notes
Foire aux questions (FAQ)
|
|
Démonstration 1: Introduction légère au langage C
COMMENT COMPILER
Deux compilateurs: cc et gcc
gcc -Wall -g foo.c -o foo
-Wall: affiche tous les messages d'avertissement
-g: produit du code interprétable par le
débogueur, gdb
-o foo: nomme l'exécutable foo (par défaut:
a.out)
TYPES DE BASE
char int float double
Note:
Pas de booléen! On utilise les entiers(0
= = faux, n'importe quoi d'autre == vrai)
unsigned: unsigned int, unsigned char
short et long: short int (short), long
int (long)
DÉCLARATION DE VARIABLES
int a;
double b, c;
Note:
Les variables automatiques définies en C
ne sont pas implicitement initialisées.
TYPES COMPLEXES
Tableaux:
int tab[5];
tab[0] = 0;
tab[1] = 1;
...
tab[4] = 4;
tab[5] = 5; /* ILLÉGAL(Compile, mais attention
aux segmentation faults!) */
int mat[2][3]
mat[0][2] = 0;
int tab[]; /* Légal, mais on ne peut accéder
aux éléments du tableau
avant d'en avoir spécifié la taille */
Pointeurs:
int *p;
Le pointeur nul est identifié par NULL. Ex: if (p == NULL)
...
Structures:
struct point {
char etiquette;
int x, y;
};
struct point A;
A.x = 0;
struct point *p_A;
p_A->x = 0; ou (*p_A).x = 0;
Chaînes de caractères:
Une chaîne de caratères est un tableau de char
terminé par le char '\0'
char mot1[5]; /* Réserve cinq "cases" en
mémoire */
char *mot2; /* Ne réserve pas
de mémoire */
CONSTANTES
1 10 -5 int
100L long
(la plupart des compilateurs ont int=long)
1.2 1.0e-8 float
1.2L double
'c' '\n' caractère
"hello" réserve 6
cases en mémoire, initialise le contenu avec hello
et retourne un pointeur vers ces cases mémoire.
ARITHMÉTIQUE
Opérateurs: + - * / %(modulo)
Opérations sur les bits: & | ^ ~ >> <<
Auto-incrémentation: ++ --
++x ou x++ => x=x+1
Important:
++x == x+1
x++ == x
Avec affectations: += -= *= /= %= &= |= ^=
x op= y <==> x = x op y
AFFECTATIONS
a=2;
a=X && Y;
*(get_ptr())=3;
1+1=2; /* Illégal, le
membre de gauche doit être une l-value(espace mémoire) */
DÉCLARATION DE FONCTIONS ET PROCÉDURES
int inc(int a)
{
return ++a;
}
void foo()
{
printf("foo");
}
VARIABLES LOCALES, GLOBALES, AUTOMATIQUES, STATIQUES, CONSTANTES
auto int x; /* Équivalent à int x; */
const int b=2;
int foo(const int c,int d)
{
int e;
static int f=2;
while(1)
{
int g=2,a=10;
if(!a) break; else
a-=g;
e++;
}
return b+e;
}
-Les variables locales ne sont utilisables que dans
le corps les englobants.
Locales: e,f,g,a
-Les variables globales sont utilisables partout
dans le programme.
Globales: x,b
-Les variables automatiques allouent et détruisent
leur espace dynamiquement, en utilisant en général la pile.
De plus, l'initialisation est faite à
chaque fois que l'espace est alloué.
Automatiques: x,e,g,a
-Les variables statiques ont leur espace mémoire
propre et définitif dès le début du programme et ne
sont
initialisées qu'une seule fois. Une
variable statique locale à l'avantage de garder sa valeur au fil
des appels,
tout en restant invisible à l'extérieur
du corps l'englobant.
Statique: f
-Les "variables" constantes ont la propriété
de ne pas pouvoir être modifiées.
Constante: b
TYPEDEF
typedef int *ptr_int;
typedef int frequences[256];
typedef struct LISTE_DE_CONTEXTES liste_de_contextes;
struct LISTE_DE_CONTEXTES {
contexte *element;
liste_de_contextes* prochain;
};
Variante:
typedef struct LISTE_DE_CONTEXTES {
contexte *element;
struct LISTE_DE_CONTEXTES *prochain;
} liste_de_contextes;
Utilisation des types:
ptr_int p;
frequences f;
liste_de_contextes *l;
EXPRESSIONS BOOLÉENES
! Opérateur unaire NON
&& Opérateur binaire
ET
|| Opérateur binaire OU
== Operateur binaire d'égalité
numérique
!= Raccourci: (x!=y) == !(x==y)
NOTE SUR LES && ET LES ||
Une expression contenant && ou ||
est évaluée seulement jusqu'à ce
qu'on en connaisse la valeur de vérite. Par exemple,
dans
l'expression:
if (1 || x > 0) ...
x > 0 n'est jamais évalué.
De même pour
if (0 && x > 0) ...
Utile pour faire:
if ((l != NULL) && (l->element == ...)) ...
Remarquez aussi que NULL a la valeur de vérité
faux, et donc
l'expression précédente peut s'ecrire
if (l && (l->element == ...)) ...
ÉNONCÉS DE CONTRÔLE
if (<expression>) <instruction>
if (<expression>) <instruction> else <instruction>
if (a<5) a = 0;
if (a<5)
a = 0;
if (a<5) {
a = 0;
b = 1;
}
else {
a = 10;
b = 11;
}
Note:
Aussi possible: a = a<5 ? 0 : 10;
while (<expression>) <instruction>
while (a>0) {
printf("*");
a--;
}
do <instruction> while (<expression>);
do {
printf("*");
a--;
} while (a>0)
for (<init>; <test>; <inc>) <instruction>
/* Équivalent à: */
<init>
while (<test>) {
<instruction>
<inc>
}
for (i=0; i<tablen; i++)
tab[i] = 0;
BREAK ET CONTINUE
break, utilisé dans un while,
do,
for ou switch, fait sortir de la boucle.
continue retourne au début de la boucle.
Dans le cas d'un while, on
teste immédiatement la condition d'arrêt de la
boucle. Dans le cas
d'un for, on exécute d'abord l'incrémentation.
SWITCH
switch (note) {
case 'A': case 'B': case 'C': case 'D':
++nb_reussite;
break;
case 'E': case 'F':
++nb_echecs;
break;
default:
printf("note non reconnue");
break;
}
EXIT
void exit(int status);
Met fin à l'exécution en envoyant le signal status.
On peut
aussi utiliser un return si on est dans le main().
ENTRÉES/SORTIES DE BASE
stdin, entrée standard, normalement le clavier
stdout, sortie standard, normalement l'écran
stderr, erreur standard, normalement l'écran
sous UNIX, tp1 -n 3 < entree.txt > sortie.txt
=> stdin = le fichier entree.txt
=> stdout = le fichier sortie.txt
=> stderr demeure l'écran
Redirection de stderr:
tp1 -n 3 < entree.txt > sortie.txt 2> erreurs.txt
La fonction printf écrit au stdout.
printf("foo");
float entropie;
printf("L'entropie est %f", entropie);
fprintf(FILE *fp, ...) /* fait comme printf, mais écrit
dans le fichier fp. */
Exemple:
fprintf(stderr,"Erreur, mauvais parametre.\n");
Les spécificateurs principaux:
%d imprime un int.
%6d imprime un int sur
6 espaces.
%f imprime un float.
%6.2f imprime un float sur 6 espaces dont
2 après le point.
%c imprime un char.
%s imprime un string (char
*).
Important:
Pas de typecheck sur les arguments à imprimer!
Caractères speciaux:
\n newline
\t tab
PRÉPROCESSEUR
Inclusion de sources:
#include <stdio.h> /* Cherche stdio.h
dans des répertoires prédefinis */
#include "debug.h" /* Cherche debug.h dans
le répertoire courant */
Les principales librairies de fonctions:
stdio.h routines d'entrées/sorties
stdlib.h fonctions et constantes diverses (exit, NULL,
...)
string.h manipulation de chaînes de caractères
math.h fonctions mathématiques
Constantes et macros:
#define MAX_TABLE 10
/* MAX_TABLE devient un synonyme de 10 */
#define MAX(a,b) ((a) > (b) ? (a) : (b))
/* b = MAX(c,d/2); => b = ((c)
> (d/2) ? (c) : (d/2)); */
La convention est d'utiliser des majuscules pour les noms des
macros
pour les différencier des fonctions.
Remarquez les parenthèses autour de a et b. Sans elles,
on pourrait
avoir des problemes.
Exemple:
#define MAX(a,b) a > b ? a : b
b = MAX(2,3)*5 => b = 2 > 3 ?
2 : 3*5
Ou, en plus simple,
#define MUL(a,b) a * b
MUL(1 + 2, 3); => 1 + 2 * 3;
ce qui donne 7 plutôt que le 9 voulu.
MANIPULATION DE CHAÎNES DE CARACTÈRES
Quelques fonctions définies dans string.h:
strcpy(char *destination, char *source);
copie source dans destination.
!!!ATTENTION: destination doit être assez grand pour pouvoir
contenir source. Le compilateur et la fonction ne font aucune
vérification, c'est au programmeur de s'assurer que la
taille de
destination est suffisante.
Ex:
char ch1[5];
strcpy(ch1, "foo");
/* OK */
strcpy(ch1, "foobar"); /* incorrect
*/
strcpy(ch1, "fooba"); /*
incorrect, car il ne reste plus d'espace
dans ch1 pour le caractère '\0' */
strncpy(char *destination, char *source, int n);
/* Copie les n premiers caractères de source
dans destination.*/
strlen(char *string);
/* Retourne la longueur de la chaîne,
sans compter le '\0' */
Ex:
strlen("foo") == 3;
strcat(char *s1, char *s2);
/* Concatène s2 à s1. Le premier
caractère de s2 écrase le '\0' à la fin de s1. */
Ex:
char ch1[20];
strcpy(ch1, "foo");
strcat(ch1, "bar");
/* => ch1 == foobar */
int strcmp(char *s1, char *s2);
/* retourne <0 si s1 < s2
0 si s1 == s2
>0 si s1 > s2 */
Travail pratique (pas noté mais fortement recommandé)
1) Implémentation de quelques fonctions de la librairie string.h
Implementez (pas nécessairement avec des pointeurs) les fonctions:
strlen , strcpy et strcat
|
2) Tri d'un tableau d'entiers
- Écrire une méthode qui affiche un à un les éléments d'un tableau
d'entiers passé en paramètre.
Voici le squelette (beurk) de cette méthode, où
nb indique le nombre d'éléments à afficher:
void affiche(int tab[], int nb) {
/* a vous de completer */
}
- Écrire une méthode qui permet de trier (en ordre croissant)
un tableau d'entiers passé en paramètre.
Voici le squelette (beurk) de cette méthode, où nb indique le nombre d'éléments à trier:
void tri(int tab[], int nb) {
/* a vous de completer */
}
Voici un exemple d'utilisation de cette méthode:
#include <stdio.h> /* entree/sortie standard */
/*
* mettre ici votre code pour les méthodes affiche et tri
*/
int tab1[] = {12, 15, 5, 8, 10};
int tab2[] = {10, 15, 12};
int main(int argc, char *argv[]) {
printf("avant "); affiche(tab1,5);
tri(tab1,5);
printf("apres "); affiche(tab1,5);
printf("avant "); affiche(tab2,3);
tri(tab2,3);
printf("apres "); affiche(tab2,3);
return 0; /* signale que tout va bien */
}
|
3) Tri d'un tableau de structures
Soit la structure:
typedef struct {
char nom[100];
int age;
} Personne;
Dans cette exercice, on suppose que les tableaux de
Personne ont pour dernier élément, une personne factice
dont l'âge est 0 . Ce dernier élément est simplement un
marqueur de fin de table.
Exemple:
Personne tab[] = {
{"jean",27},
{"valérie",23},
{"fabienne",23},
{"paul",23},
{"isabelle",24},
{"nop",0} /* marqueur de fin de tableau (a ne pas afficher) */
};
- Écrire une méthode qui permet d'afficher un tableau de
Personne . On ne passera pas la taille du tableau comem dans l'exercice précédant.
- Écrire une méthode qui permet de trier un tableau de
Personne passé en paramètre en ordonnant les personnes de la plus jeune à la plus vieille.
- Écrire une méthode qui permet de trier un tableau de
Personne passé en paramètre en ordonnant les personnes en fonction de leur nom.
- Écrire une méthode qui permet de trier un tableau de
Personne passé en paramètre en ordonnant les personnes en fonction de leur âge (premier critère) et de leur nom (deuxième critère à appliquer lorsque deux personnes ont le même âge).
En clair, voici l'ordre des éléments après application de ce tri pour le tableau donné en exemple:
{"fabienne",23}, {"paul",23}, {"valérie",23}, {"isabelle",24}, {"jean",27}, {"nop",0}
|
4) Un peu plus difficile
Note: cet exercice peut-être évité de tous ceux qui programment
en C pour la première fois.
Vous devez en avoir marre de réécrire une méthode de tri pour
chaque critère de tri.
Consultez le manuel en ligne (man qsort ) de manière à
utiliser la méthode qsort .
|
[top] |