« INFO517 : Programmation C » : différence entre les versions
m (→Les exemples vus en cours : taille du bloc alloué -> 1024) |
|||
(38 versions intermédiaires par 5 utilisateurs non affichées) | |||
Ligne 1 : | Ligne 1 : | ||
Cours du semestre 5 de la licence STIC INFO. |
Cours du semestre 5 de la licence STIC INFO. |
||
Responsable pour |
* Responsable pour 2010--2011: [http://www.lama.univ-savoie.fr/~lachaud Jacques-Olivier Lachaud] |
||
* Jacques-Olivier Lachaud (C/TD/TP), Xavier Provençal (TP) |
|||
Pensez à consulter les [[Comment compiler le C ?|indications pour compiler un petit programme sur une machine des salles de TP]]. |
Pensez à consulter les [[Comment compiler le C ?|indications pour compiler un petit programme sur une machine des salles de TP]]. |
||
== Quelques ressources pour l'étudiant (2010-2011) == |
|||
# Notes de cours [http://www.lama.univ-savoie.fr/~lachaud/Cours/INFO517/Cours/notes-de-cours.ps PostScript] [http://www.lama.univ-savoie.fr/~lachaud/Cours/INFO517/Cours/notes-de-cours.pdf PDF] |
|||
# Fiches de TD |
|||
#* TD 1 et 2 : tableaux, entrées-sorties [http://www.lama.univ-savoie.fr/~lachaud/Cours/INFO517/TDs/td-1.ps PostScript] [http://www.lama.univ-savoie.fr/~lachaud/Cours/INFO517/TDs/td-1.pdf PDF] |
|||
#* TD 3 : exercices sur les pointeurs [http://www.lama.univ-savoie.fr/~lachaud/Cours/INFO517/TDs/td-2.ps PostScript] [http://www.lama.univ-savoie.fr/~lachaud/Cours/INFO517/TDs/td-2.pdf PDF] |
|||
# TPs et autres travaux pratiques [http://www.lama.univ-savoie.fr/~lachaud/Cours/INFO517/Tests/doc/html/index.html Pages des TPs] |
|||
#* Pour la première fois, on pourra aussi regarder la page [[Comment_compiler_le_C_%3F]] |
|||
#* Si vous n'accédez pas aux pages "manual" en salle TP, on les trouve en ligne : [[http://www.linux-france.org/article/man-fr/ Manual pages]] |
|||
# Autres ressources |
|||
N'hésitez pas à contribuer au wiki, et en particulier à cette page: |
N'hésitez pas à contribuer au wiki, et en particulier à cette page: |
||
Ligne 12 : | Ligne 24 : | ||
problème et ce qui vous a permis de mieux comprendre. |
problème et ce qui vous a permis de mieux comprendre. |
||
== Déroulement (2010-2011) == |
|||
== Fonctionnement == |
|||
Ceci n'est qu'une prévision. |
|||
* (Cours 1): lundi 20 septembre. Langage C, intérêts et défauts. Compilation. Eléments de base du langage (types simples, variables, expressions). (=> I.5). |
|||
* (TD 1): mercredi 29 septembre. Instructions et structures de contrôle usuelles (conditionnelles, boucles) (=> I.10) |
|||
* (TD 2): jeudi 30 septembre. TD 1 tableaux, fonctions en C, E/S simples. |
|||
* (Cours 2): vendredi 1er octobre. Fonctions, passage de paramètres, pointeurs (=> II.5). |
|||
* (TD 3): vendredi 1er octobre. TD 1 tableaux, fonctions en C, E/S simples (II). |
|||
* (Cours 3): lundi 4 octobre. Fonctions, passage de paramètres, pointeurs, allocation dynamique (=> II.10) |
|||
* (Cours 4): lundi 4 octobre. Un exemple complet : les piles en C (II.11). |
|||
* (TP 1): mercredi 6 octobre. Boucles, puissance 4, tracés avec gnuplot, récursivité. |
|||
* (TD 4): vendredi 8 octobre. Exercices simples sur les pointeurs. |
|||
* (TD 5): lundi 11 octobre. Pile d'exécution. Structures auto-référents. Début skip-liste. |
|||
* (TP 2): mercredi 13 octobre. Tetris texte. |
|||
* (TD 6): vendredi 15 octobre. Skip-listes. Compilation séparée et bonnes habitudes de développement C. |
|||
* (TP 3): jeudi 21 octobre. Tetris graphique avec GTK. |
|||
== Historique == |
|||
* Responsable pour 2009--2010: Emilie Charrier (C/TD/TP) |
|||
* Responsable pour 2008--2009: Lionel Vaux (C/TD/TP) |
|||
== Ressources pour l'étudiant (avant 2010) == |
|||
Cet enseignement comprendra 10 séances de cours/TD (1h30) et 3 séances de TP (4h). |
Cet enseignement comprendra 10 séances de cours/TD (1h30) et 3 séances de TP (4h). |
||
Ligne 26 : | Ligne 61 : | ||
== Objectifs du cours == |
=== Objectifs du cours === |
||
==== Cours/TD ==== |
|||
* Principes généraux et particularités du langage (programmation impérative, typage fort, adressage mémoire) |
|||
* Principes généraux et particularités du langage: programmation impérative, typage fort à la compilation, adressage mémoire explicite |
|||
* Syntaxe |
|||
* Syntaxe de base |
|||
* Bibliothèque standard (pour les entrées-sorties et l'interaction avec le système d'exploitation) |
|||
* Bibliothèque standard: entrée-sorties et interaction avec le système d'exploitation |
|||
* Gestion de la mémoire |
|||
* C avancé: |
|||
** allocation dynamique |
|||
** modèle mémoire (pile, tas, code) |
|||
** pointeurs sur structures |
|||
** pointeurs sur fonctions |
|||
* Bonnes pratiques |
* Bonnes pratiques |
||
* Outils et concepts: |
|||
** automatisation de la compilation (make), |
|||
** analyse de l'exécution et déboguage (gdb, valgrind), |
|||
** documentation (doxygen), |
|||
** boîte à outils graphique (gtk+) |
|||
== Séances == |
|||
=== Cours/TD 1 : lundi 22 septembre 2008 === |
|||
Présentation tout-en-un. |
|||
Le but de ce cours est de fournir le minimum vital aux étudiants pour: |
|||
* écrire un programme simple et court utilisant les types de base |
|||
* le compiler et l'exécuter |
|||
* trouver de la documentation |
|||
Après cette première séance, les étudiants devraient être capable de s'amuser un peu avec le langage. |
|||
==== Les exemples vus en cours ==== |
|||
<tt>bateau.c</tt> |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
int main () { |
|||
/* Écrit une chaîne */ |
|||
puts("Bateau !"); |
|||
/* Renvoie la valeur de sortie en cas de succès */ |
|||
return 0; |
|||
} |
|||
</source> |
|||
<tt>euros-francs-v1.c</tt> |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
/* Écrit une table de conversion euros/francs |
|||
* pour euros = 0, 5, 10, ..., 100 : |
|||
* version initiale */ |
|||
main() |
|||
{ |
|||
int euros, euros_max, pas ; |
|||
float francs, un_euro ; |
|||
un_euro = 6.55957 ; /* taux de conversion */ |
|||
pas = 5 ; /* pas d'itération */ |
|||
euros = 0 ; /* valeur initiale */ |
|||
euros_max = 100 ; /* valeur maximale */ |
|||
while (euros <= euros_max) { |
|||
francs = un_euro * euros ; |
|||
printf("%d\t%f\n", euros, francs) ; |
|||
euros = euros + pas ; |
|||
} |
|||
} |
|||
</source> |
|||
<tt>euros-francs-v2.c</tt> |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
/* Écrit une table de conversion euros/francs |
|||
* pour euros = 0, 5, 10, ..., 100 : |
|||
* correction de l'alignement */ |
|||
main() |
|||
{ |
|||
int euros, euros_max, pas ; |
|||
float francs, un_euro ; |
|||
un_euro = 6.55957 ; /* taux de conversion */ |
|||
pas = 5 ; /* pas d'itération */ |
|||
euros = 0 ; /* valeur initiale */ |
|||
euros_max = 100 ; /* valeur maximale */ |
|||
while (euros <= euros_max) { |
|||
francs = un_euro * euros ; |
|||
printf("%3d\t%6.2f\n", euros, francs) ; |
|||
euros = euros + pas ; |
|||
} |
|||
} |
|||
</source> |
|||
<tt>euros-francs-v3.c</tt> |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
/* Écrit une table de conversion euros/francs |
|||
* pour euros = 0, 5, 10, ..., 100 : |
|||
* avec un `for' */ |
|||
main() |
|||
{ |
|||
int euros ; |
|||
for (euros = 0 ; euros <= 100 ; euros = euros + 5) |
|||
printf("%3d\t%6.2f\n", euros, 6.55957*euros) ; |
|||
} |
|||
</source> |
|||
<tt>euros-francs-v4.c</tt> |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
#define UN_EURO 6.55957 /* un euro en francs */ |
|||
/* Écrit une table de conversion euros/francs |
|||
* pour euros = 0, 5, 10, ..., 100 : |
|||
* définition pour le préprocesseur */ |
|||
main() |
|||
{ |
|||
int euros ; |
|||
for (euros = 0 ; euros <= 100 ; euros = euros + 5) |
|||
printf("%3d\t%6.2f\n", euros, UN_EURO*euros) ; |
|||
} |
|||
</source> |
|||
<tt>arrondi.c</tt> |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
#define INCR 0.00001 /* incrément pour le test de précision */ |
|||
#define NUM 100000 /* nombre de pas */ |
|||
/* Calcule INCR*NUM en ajoutant NUM fois INCR à 0 */ |
|||
main() |
|||
{ |
|||
float accu ; |
|||
int i ; |
|||
accu = 0 ; |
|||
for (i=0 ; i < NUM ; i=i+1) |
|||
accu = accu + INCR ; |
|||
printf("%f=%f?\n",NUM*INCR,accu) ; |
|||
} |
|||
</source> |
|||
<tt>arrondi-double.c</tt> (cette variante a été signalée pendant les rappels de la |
|||
deuxième séance) |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
#define INCR 0.00001 /* incrément pour le test de précision */ |
|||
#define NUM 100000 /* nombre de pas */ |
|||
/* Calcule INCR*NUM en ajoutant NUM fois INCR à 0. |
|||
* Cette version utilise un accumulateur de type `double' |
|||
* pour limiter les erreurs d'arrondi */ |
|||
main() |
|||
{ |
|||
double accu ; // On calcule en double précision |
|||
int i ; |
|||
accu = 0 ; |
|||
for (i=0 ; i < NUM ; i=i+1) |
|||
accu = accu + INCR ; |
|||
printf("%f=%f?\n",NUM*INCR,accu) ; |
|||
} |
|||
</source> |
|||
<tt>copie-v1.c</tt> |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
/* Copie l'entrée standard sur la sortie standard */ |
|||
main() |
|||
{ |
|||
int c; |
|||
c = getchar(); |
|||
while (c != EOF) { |
|||
putchar(c); |
|||
c = getchar(); |
|||
} |
|||
} |
|||
</source> |
|||
<tt>copie-v2.c</tt> |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
/* Copie l'entrée standard sur la sortie standard : |
|||
* assignation comme valeur */ |
|||
main() |
|||
{ |
|||
int c; |
|||
while ((c = getchar()) != EOF) { |
|||
putchar(c); |
|||
} |
|||
} |
|||
</source> |
|||
==== Exercices pour le 29 septembre ==== |
|||
* Au choix: |
|||
*# Sur la machine et le système de votre choix, écrire et compiler un programme C (par exemple <tt>bateau.c</tt>), puis envoyer le fichier source et le binaire obtenu à l'adresse <tt>lionel.vaux@univ-savoie.fr</tt>. |
|||
*# Ne pas y parvenir et alors me contacter au plus tôt pour y remédier. Ensuite revenir au choix 1, évidemment. |
|||
* Modifier l'un des fichiers <tt>euros-francs-v?.c</tt> pour afficher une ligne d'en-tête alignée sur les résultats (et quelques fioritures). C'est-à-dire que la sortie doit ressembler à: |
|||
Euros: Francs: |
|||
0 -> 0.00 |
|||
5 -> 32.80 |
|||
10 -> 65.60 |
|||
15 -> 98.39 |
|||
20 -> 131.19 |
|||
... |
|||
* Écrire un programme <tt>francs-euros.c</tt> qui affiche une table de conversion dans le sens contraire (les comptes ronds sont en francs). |
|||
* Écrire un programme qui affiche la valeur entière, de type <tt>int</tt>, de <tt>EOF</tt> (vérifier qu'elle n'est pas dans l'intervalle entier <tt>[0..255]</tt>). |
|||
* Écrire un programme qui affiche la valeur entière du « caractère » <tt>€</tt> (il est possible que vous ne compreniez pas très bien ce qui vous arrive: on en parlera). |
|||
* Écrire un programme qui compte le nombre de caractères (au sens de <tt>getchar()</tt>) dans un fichier. |
|||
==== Solutions possibles pour les exercices ==== |
|||
Pour les variations sur <tt>euros-francs-v?.c</tt>, voilà un programme qui rassemble un peu tout: |
|||
<tt>conversion.c</tt> |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
#define UN_EURO 6.55957 /* un euro en francs */ |
|||
/* Écrit une table de conversion euros/francs |
|||
* pour euros = 0, 5, 10, ..., 100 : |
|||
* définition pour le préprocesseur */ |
|||
main() |
|||
{ |
|||
int euros ; |
|||
int francs ; |
|||
printf("Euros:\t\tFrancs:\n") ; |
|||
for (euros = 0 ; euros <= 100 ; euros = euros + 5) |
|||
printf(" %3d\t\t %6.2f\n", euros, UN_EURO*euros) ; |
|||
printf("\nFrancs:\t\tEuros:\n") ; |
|||
for (francs = 0 ; francs <= 100 ; francs = francs + 5) |
|||
printf(" %3d\t\t %6.2f\n", francs, francs/UN_EURO) ; |
|||
} |
|||
</source> |
|||
Un programme qui affiche la valeur entière (type <tt>int</tt>) de <tt>EOF</tt>: |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
/* Écrit la valeur entière de EOF */ |
|||
main() |
|||
{ |
|||
printf("%d\n",EOF) ; |
|||
} |
|||
</source> |
|||
On sauve ça dans <tt>EOF.c</tt>, puis on compile avec |
|||
$ gcc -Wall -o EOF EOF.c |
|||
Les erreurs produites sont standard (mauvais prototype pour <tt>main</tt>). |
|||
L'exécution sur ma machine donne: |
|||
$ ./EOF |
|||
-1 |
|||
La valeur entière de <tt>€</tt>, sur le même modèle: |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
main () { |
|||
int euro = '€' ; |
|||
printf("%d\n",euro) ; |
|||
} |
|||
</source> |
|||
On sauve ça dans <tt>euro.c</tt>, puis on compile avec: |
|||
$ gcc -Wall -o euro euro.c |
|||
ce qui produit les avertissements: |
|||
euro.c:3: attention : return type defaults to «int» |
|||
euro.c:4:13: attention : constante caractère multi-caractères |
|||
euro.c: Dans la fonction «main» : |
|||
euro.c:7: attention : control reaches end of non-void function |
|||
La deuxième ligne est le premier signe que quelque chose de bizarre est à l'œuvre. À l'exécution, on obtient: |
|||
$ ./euro |
|||
14844588 |
|||
Savez-vous expliquer ce qui se passe ? |
|||
Pour compter le nombre de caractères: ce qu'on a vu en cours fait mieux. |
|||
=== Cours/TD 2 : lundi 29 septembre 2008 === |
|||
Rappels et précisions; fonctions; tableaux. |
|||
==== Les exemples vus en cours ==== |
|||
<tt>puissance-v1.c</tt> |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
#define MAXEXP 16 |
|||
/* Fonction puissance(a: int, b: int) : int |
|||
* renvoie a à la puissance b, si b positif, 1 sinon */ |
|||
int puissance (int a, int b) { |
|||
int i, ret ; |
|||
ret = 1 ; |
|||
for (i=0; i<b; ++i) /* ++i incrémente i */ |
|||
ret = a * ret ; |
|||
return ret ; |
|||
} |
|||
/* Procédure affiche(a: int, b: int) |
|||
* formatte le résultat de puissance(a,b) */ |
|||
void affiche(int a, int b) { |
|||
printf("%2d^%2d = %10d\n",a,b,puissance(a,b)) ; |
|||
} |
|||
/* Fonction principale: affiche les puissances de 2, de 1 à 2^MAXEXP */ |
|||
void main () { |
|||
int i ; |
|||
for (i=0; i<=MAXEXP; ++i) |
|||
affiche(2,i) ; |
|||
} |
|||
</source> |
|||
<tt>puissance-v2.c</tt> |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
#define MAXEXP 16 |
|||
/* Prototype de la fonction puissance(a: int, b: int) : int |
|||
*/ |
|||
int puissance (int a, int b) ; |
|||
/* Procédure affiche(a: int, b: int) |
|||
* formatte le résultat de puissance(a,b) */ |
|||
void affiche(int a, int b) { |
|||
printf("%2d^%2d = %10d\n",a,b,puissance(a,b)) ; |
|||
} |
|||
/* Fonction principale: affiche les puissances de 2, de 1 à 2^MAXEXP */ |
|||
void main () { |
|||
int i ; |
|||
for (i=0; i<=MAXEXP; ++i) |
|||
affiche(2,i) ; |
|||
} |
|||
/* Code de la fonction puissance : |
|||
* puissance(a,b) renvoie a à la puissance b, si b positif, 1 sinon */ |
|||
int puissance (int a, int b) { |
|||
int ret ; |
|||
ret = 1 ; |
|||
/* On utilise l'argument b comme une variable locale */ |
|||
for (/* pas d'initialisation nécessaire */; b>0; --b) |
|||
ret = a * ret ; |
|||
return ret ; |
|||
} |
|||
</source> |
|||
<tt>fibonacci.c</tt> |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
#define MAX 30 |
|||
/* calcule u[n], où u[0]=u[1]=1 et u[n+2]=u[n+1]+u[n] */ |
|||
int fibo (int n, int rec) { |
|||
if (n<=1) return 1 ; |
|||
else return (fibo(n-1,rec+1)+fibo(n-2,rec+1)) ; |
|||
} |
|||
/* affiche fibo(n) pour n de 0 à MAX */ |
|||
int main () { |
|||
int i ; |
|||
for (i=0;i<=MAX;++i) |
|||
printf("fibo(%2d)=%8d\n",i,fibo(i,0)) ; |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
<tt>tracerec.c</tt> |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
#define N 15 |
|||
/* Procédure blanks(n: int) |
|||
* insère n espaces. */ |
|||
void blanks (int n) { |
|||
for(;n>0;--n) putchar(' ') ; |
|||
} |
|||
/* Calcule fibo(n) (voir fibo.c) et affiche le résultat. |
|||
* Cet affichage dans la fonction permet de tracer le |
|||
* flot de récursion (l'ordre dans lequel les appels se font). |
|||
* |
|||
* On représente la profondeur de récursion par l'indentation, |
|||
* au moyen du second argument : tous les affichages sont décalés |
|||
* de rec espaces. L'entier rec est incrémenté dans l'appel récursif, |
|||
* et on démarre avec rec=0 dans main() |
|||
* */ |
|||
int fibo_rec (int n, int rec) { |
|||
blanks(rec) ; printf("fibo(%2d)=?\n",n) ; |
|||
if (n<=1) { |
|||
blanks(rec) ; printf("fibo(%2d)=1\n",n) ; |
|||
return 1 ; |
|||
} else { |
|||
/* On peut déclarer des variables locales à un bloc */ |
|||
int ret=fibo_rec(n-1,rec+1)+fibo_rec(n-2,rec+1) ; |
|||
blanks(rec) ; printf("fibo(%2d)=%d\n",n,ret) ; |
|||
return ret ; |
|||
} |
|||
} |
|||
/* Calcule fibo(N) avec affichage des étapes de récursion */ |
|||
int main () { |
|||
fibo_rec(N,0) ; |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
<tt>segfault.c</tt> |
|||
<source lang="c"> |
|||
/* CE CODE EST VOLONTAIREMENT FAUTIF ! */ |
|||
/* Notre première erreur de segmentation: |
|||
* les appels récursifs s'empilent sans limite et on sort de la mémoire |
|||
* disponible. */ |
|||
#include<stdio.h> |
|||
#define MAX 30 |
|||
/* calcule u[n], où u[0]=u[1]=1 et u[n+2]=u[n+1]+u[n] */ |
|||
int fibo (int n, int rec) { |
|||
return (fibo(n-1,rec+1)+fibo(n-2,rec+1)) ; |
|||
} |
|||
/* affiche fibo(n) pour n de 0 à MAX */ |
|||
int main () { |
|||
int i ; |
|||
for (i=0;i<=MAX;++i) |
|||
printf("fibo(%2d)=%8d\n",i,fibo(i,0)) ; |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
<tt>textstat.c</tt> |
|||
<source lang="c"> |
|||
#include<stdio.h> |
|||
/* En théorie, les valeurs entières des caractères |
|||
* sont dans un intervalle non précisé par la norme. |
|||
* |
|||
* Dans cet exemple, on considère que les caractères sont des entiers sur 8 |
|||
* bits non signés (cf. CHAR_MIN, CHAR_MAX). |
|||
* |
|||
* Ceci était passé sous silence lors du cours. |
|||
* |
|||
*/ |
|||
#define CHAR_MIN 0 |
|||
#define CHAR_MAX 255 |
|||
/* CHAR_NUM = CHAR_MAX - CHAR_MIN + 1 */ |
|||
#define CHAR_NUM 256 |
|||
/* Une macro évidente */ |
|||
void aff (char c, int n) { printf("%c:%2d ",c,n) ; } |
|||
/* Compte le nombre d'occurrences de chaque caractère |
|||
* et affiche certains résultats */ |
|||
int main() { |
|||
int c, i ; |
|||
int nb [CHAR_NUM] ; |
|||
/* Mise à zéro */ |
|||
for (i=0;i<CHAR_NUM;++i) |
|||
nb[i]=0 ; |
|||
/* Calcul */ |
|||
while((c=getchar()) != EOF) |
|||
++nb[CHAR_MIN+c] ; |
|||
/* Affichage */ |
|||
/* Les lettres minuscules sont consécutives de 'a' à 'z'. */ |
|||
printf("Minuscules:\n") ; |
|||
for (c='a'; c<='z' ; ++c) |
|||
aff(c,nb[CHAR_MIN+c]) ; |
|||
/* De même pour les majuscules de 'A' à 'Z'. */ |
|||
printf("\nMajuscules:\n") ; |
|||
for (c='A'; c<='Z' ; ++c) |
|||
aff(c,nb[CHAR_MIN+c]) ; |
|||
/* Et les chiffres de '0' à '9'. */ |
|||
printf("\nChiffres:\n") ; |
|||
for (c='0'; c<='9' ; ++c) |
|||
aff(c,nb[CHAR_MIN+c]) ; |
|||
printf("\nSauts de lignes: %d\n",nb[CHAR_MIN+'\n']) ; |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
==== Exercices pour le 6 octobre ==== |
|||
===== Sur les fonctions ===== |
|||
* Réécrire le programme [[INFO517#Solutions_possibles_pour_les_exercices|<tt>conversion.c</tt>]] en utilisant une fonction pour l'affichage. Rendre ce programme plus modulaire en écrivant une procédure avec le prototype <tt>void francs_euros (int min, int max, int pas)</tt> qui afffiche la conversion de <tt>n</tt> francs en euros pour <tt>n</tt> variant de <tt>min</tt> à <tt>max</tt> par pas de <tt>pas</tt> et une procédure <tt>void euros_francs (int min, int max, int pas)</tt> qui affiche la même chose mais dans l'autre sens de conversion. |
|||
* Écrire une fonction <tt>int fact (int n)</tt> qui calcule la [http://fr.wikipedia.org/wiki/Factorielle factorielle] de <tt>n</tt> de deux manières différentes: une par récursion, une en utilisant une boucle. Essayer ces fonctions sur des valeurs pas trop petites et détecter quand ça dégénère. |
|||
===== Sur les tableaux et les chaînes ===== |
|||
* Écrire une procédure <tt>void aff (int t[], int n)</tt> qui affiche les <tt>n</tt> premiers éléments du tableau <tt>t</tt>. |
|||
* On donne le programme: |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
void incr (int n) { |
|||
++n ; |
|||
} |
|||
void incr_t0 (int t[]) { |
|||
++t[0] ; |
|||
} |
|||
int main () { |
|||
int n = 0 ; |
|||
int t[1] = { 0 } ; |
|||
incr(n) ; |
|||
incr_t0(t) ; |
|||
printf("n: %d\n",n) ; |
|||
printf("t[0]: %d\n",t[0]) ; |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
Compiler et exécuter ce programme. Que remarque-t-on ? Qu'en déduire sur les tableaux comme arguments de fonctions. |
|||
* Que fait le programme <tt>fin_chaine.c</tt> suivant ? Expliquer. |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
int main () { |
|||
printf("Bateau.\n\0Et puis aussi...") ; |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
* Écrire un programme qui affiche les lignes données en entrée seulement si elles comportent plus de 20 caractères. Penser à structurer intelligemment. |
|||
* |
|||
* Écrire une fonction <tt>void pal(char s[])</tt> qui renvoie <tt>1</tt> si la chaîne <tt>s</tt> est un [http://fr.wikipedia.org/wiki/Palindrome palindrome] et <tt>0</tt> sinon. |
|||
* Écrire une procédure <tt>void renverse(char s[])</tt> qui renverse la chaîne <tt>s</tt>. |
|||
==== Solutions possibles ==== |
|||
Un exemple complet de programme utilisant les fonctions (on n'hésite pas à en rajouter): |
|||
#include <stdio.h> |
|||
#define UN_EURO 6.55957 /* un euro en francs */ |
|||
void aff_col (int a, float b) |
|||
{ |
|||
printf(" %3d\t\t %6.2f\n", a, b ) ; |
|||
} |
|||
void aff_en_tete (const char a[], const char b[]) |
|||
{ |
|||
printf("%s\t\t%s\n", a, b ) ; |
|||
} |
|||
void table_conv (float taux, int min, int max, int pas) |
|||
{ |
|||
int i ; |
|||
if (pas > 0) |
|||
/* si le pas est positif on va du min au max */ |
|||
for (i=min; i<=max; i=i+pas) |
|||
aff_col(i,taux*i) ; |
|||
else if (pas < 0) |
|||
/* si le pas est négatif on va du max au min */ |
|||
for (i=max; i>=min; i=i+pas) |
|||
aff_col(i,taux*i) ; |
|||
} |
|||
void francs_euros (int min, int max, int pas) |
|||
{ |
|||
aff_en_tete("Francs","Euros") ; |
|||
table_conv (1/UN_EURO,min,max,pas) ; |
|||
} |
|||
void euros_francs (int min, int max, int pas) |
|||
{ |
|||
aff_en_tete("Euros","Francs") ; |
|||
table_conv (UN_EURO,min,max,pas) ; |
|||
} |
|||
int main() |
|||
{ |
|||
francs_euros(10,100,10) ; |
|||
euros_francs(1,10,-1) ; |
|||
return 0 ; |
|||
} |
|||
Sur les factorielles: |
|||
#include <stdio.h> |
|||
#define MAX 30 |
|||
/* calcule n! par récurrence sur n */ |
|||
unsigned long int fact_rec (unsigned int n) { |
|||
if (n<=1) return 1 ; |
|||
else return n*fact_rec(n-1) ; |
|||
} |
|||
/* calcule n! par comme un produit itéré */ |
|||
unsigned long int fact_it (unsigned int n) { |
|||
unsigned int i ; |
|||
unsigned long int acc = 1; |
|||
for (i=2;i<=n;++i) |
|||
acc=i*acc ; |
|||
return acc ; |
|||
} |
|||
/* fonctions d'affichage */ |
|||
void aff (unsigned int n, unsigned long int res, const char nom[]) { |
|||
printf("On calcule %u!=%lu par %s\n", n, res, nom) ; |
|||
} |
|||
void aff_fact_rec (unsigned int n) { |
|||
aff(n,fact_rec(n),"fact_rec") ; |
|||
} |
|||
void aff_fact_it (unsigned int n) { |
|||
aff(n,fact_it(n),"fact_it") ; |
|||
} |
|||
/* affiche fact_rec(n) et fact_it(n) pour n de 0 à MAX */ |
|||
/* noter que malgré toutes nos précautions, ça dégénère vite, |
|||
* car n! croit vite */ |
|||
int main () { |
|||
unsigned int i ; |
|||
for (i=1; i<MAX; ++i) { |
|||
aff_fact_rec(i) ; |
|||
aff_fact_it(i) ; |
|||
} |
|||
return 0 ; |
|||
} |
|||
Afficher un tableau d'entiers: |
|||
/* Affiche les n premiers éléments du tableau t sous la forme |
|||
* {t[0],t[1],...,t[n-1]} |
|||
* */ |
|||
void aff (int t[], int n) { |
|||
int i ; |
|||
putchar('{') ; |
|||
for (i=0;i<n;++i) { |
|||
printf("%d",t[i]) ; |
|||
if (i!=n-1) |
|||
putchar(',') ; |
|||
} |
|||
putchar('}') ; |
|||
putchar('\n') ; |
|||
} |
|||
Afficher uniquement les lignes avec au moins 20 caractères |
|||
(ici, une version plus générique): |
|||
void ligne_au_moins (int n) { |
|||
char s[n] ; |
|||
int c = getchar() ; |
|||
while (c != EOF) { |
|||
// Dans cette boucle, on traite une ligne, qui commence par c. |
|||
int i = 0 ; |
|||
// On commence par essayer de stocker les n premiers caractères : |
|||
while (c != '\n' && c != EOF && i<n) { |
|||
s[i] = c ; |
|||
++ i ; |
|||
c = getchar() ; |
|||
} |
|||
// Si on a lu n caractères sans rencontrer de retour à la ligne |
|||
// ni de fin de fichier, on recopie et on vide la ligne. |
|||
if (i==n) { |
|||
// On recopie s |
|||
for (i=0; i<n; ++i) |
|||
putchar(s[i]) ; |
|||
// On recopie la fin de la ligne |
|||
while (c != '\n' && c != EOF) { |
|||
putchar(c) ; |
|||
c = getchar() ; |
|||
} |
|||
// Retour à la ligne, si besoin |
|||
if (c == '\n') { |
|||
putchar(c) ; |
|||
c = getchar () ; |
|||
} |
|||
} else if (c == '\n') { |
|||
// Si la ligne se termine avant le n-ième caractère, |
|||
// on passe à la suivante. |
|||
c = getchar() ; |
|||
} |
|||
} |
|||
} |
|||
Pour le test de palindrome et le renversement, on a d'abord besoin de connaître la longueur de la chaîne: |
|||
unsigned int longueur (char s[]) { |
|||
unsigned int l ; |
|||
for (l=0; s[l]!='\0'; ++l) ; |
|||
return l ; |
|||
} |
|||
int pal (char s[]) { |
|||
unsigned int l = longueur(s) ; |
|||
int i ; |
|||
int ret = 1 ; |
|||
// On parcourt la moitié du tableau (au plus) et on s'arrête si jamais |
|||
// on trouve des valeurs différentes dans des cases symétriques. |
|||
for (i=0; i<l/2 && ret ; ++i) |
|||
ret = (s[i]==s[l-i-1]) ; |
|||
return ret ; |
|||
} |
|||
void renverse (char s[]) { |
|||
unsigned int l = longueur(s) ; |
|||
int i ; |
|||
char c ; |
|||
// On parcourt la moitié du tableau (au plus). |
|||
for (i=0; i<l/2 ; ++i) { |
|||
c = s[i] ; |
|||
s[i] = s[l-i-1] ; |
|||
s[l-i-1] = c ; |
|||
} |
|||
} |
|||
=== Cours/TD 3 : lundi 6 octobre 2008 === |
|||
Exercices: [http://www.lama.univ-savoie.fr/~vaux/ens/INFO517-TD1.pdf feuille 1]. |
|||
==== Devoir à la maison pour le 13 octobre ==== |
|||
Le sujet: [http://www.lama.univ-savoie.fr/~vaux/ens/INFO517-DM1.pdf <tt>INFO517-DM1.pdf</tt>] et les fichiers sources [http://www.lama.univ-savoie.fr/~vaux/ens/dm1.c <tt>dm1.c</tt>] et [http://www.lama.univ-savoie.fr/~vaux/ens/mat.c <tt>mat.c</tt>]. |
|||
=== Cours/TD 4 : lundi 13 octobre 2008 === |
|||
Représentation de la mémoire: pointeurs. |
|||
==== Les exemples vus en cours ==== |
|||
Appel par adresse: |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
void inc (int *i) { |
|||
++(*i) ; |
|||
} |
|||
int main () { |
|||
int n = 0 ; |
|||
printf("%d\n",n) ; |
|||
inc(&n) ; |
|||
printf("%d\n",n) ; |
|||
return n ; |
|||
} |
|||
</source> |
|||
Rappel (tableaux): |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
void inc (int t[]) { |
|||
t[0]++ ; |
|||
} |
|||
int main () { |
|||
int t[] = { 0 } ; |
|||
printf("%d\n",t[0]) ; |
|||
inc(t) ; |
|||
printf("%d\n",t[0]) ; |
|||
return t[0] ; |
|||
} |
|||
</source> |
|||
Les tableaux sont des pointeurs particuliers: |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
void aff (char *s) { |
|||
while (*s != '\0') { |
|||
putchar(*s) ; |
|||
s++ ; |
|||
} |
|||
} |
|||
int main () { |
|||
aff("bateau\n") ; |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
Affichage des éléments d'un tableau d'entiers |
|||
(c'est pareil): |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
/* affiche les n premiers éléments de t */ |
|||
void aff (int *t, int n) { |
|||
putchar('{') ; |
|||
for (;n>0;n--) { |
|||
printf("%d",*t) ; |
|||
if (n>1) |
|||
putchar(',') ; |
|||
t++ ; |
|||
} |
|||
putchar('}') ; |
|||
putchar('\n') ; |
|||
} |
|||
int main () { |
|||
int t[] = {1,7,3,4,0,-1} ; |
|||
aff(t,3) ; |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
Attention: rien ne contrôle le fait qu'on est bien dans le tableau. |
|||
C'est-à-dire que ce qui suit est un programme C valide, qui compile et s'exécute: |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
/* affiche les n premiers éléments de t */ |
|||
void aff (int *t, int n) { |
|||
putchar('{') ; |
|||
for (;n>0;n--) { |
|||
printf("%d",*t) ; |
|||
if (n>1) |
|||
putchar(',') ; |
|||
t++ ; |
|||
} |
|||
putchar('}') ; |
|||
putchar('\n') ; |
|||
} |
|||
int main () { |
|||
int t[] = {1,7,3,4,0,-1} ; |
|||
aff(t,7) ; // PROBLÈME ! |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
On obtient par exemple: |
|||
{1,7,3,4,0,-1,-1077769696} |
|||
Toutefois, il ne faut pas pousser le bouchon trop loin: on finit par écrire |
|||
dans des zones qui ne sont pas allouées en mémoire. Ce programme, bien que |
|||
valide en C, plante: |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
/* affiche les n premiers éléments de t */ |
|||
void aff (int *t, int n) { |
|||
putchar('{') ; |
|||
for (;n>0;n--) { |
|||
printf("%d",*t) ; |
|||
if (n>1) |
|||
putchar(',') ; |
|||
t++ ; |
|||
} |
|||
putchar('}') ; |
|||
putchar('\n') ; |
|||
} |
|||
int main () { |
|||
int t[] = {1,7,3,4,0,-1} ; |
|||
aff(t,32000) ; // PROBLÈME ! |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
C'est notre deuxième erreur de segmentation: on y reviendra, |
|||
en précisant où sont les choses en mémoire. |
|||
On peut afficher les adresses d'un peu tout. Ici, par exemple, celles des |
|||
arguments des fonctions qui se retrouvent sur la pile. |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
void f (char a, char b) { |
|||
printf("char:\n") ; |
|||
printf("&a: %u\n&b: %u\n", &a,&b) ; |
|||
printf("a: %u\nb: %u\n", a,b) ; |
|||
} |
|||
void g (int a, int b) { |
|||
printf("int:\n") ; |
|||
printf("&a: %u\n&b: %u\n", &a,&b) ; |
|||
printf("a: %u\nb: %u\n", a,b) ; |
|||
} |
|||
void h (long double a, long double b) { |
|||
printf("long double:\n") ; |
|||
printf("&a: %u\n&b: %u\n", &a,&b) ; |
|||
printf("a: %Lf\nb: %Lf\n", a,b) ; |
|||
} |
|||
int main () { |
|||
f(0,1) ; |
|||
g(0,1) ; |
|||
h(0,1) ; |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
À l'exécution, on remarque que l'écart entre ces adresses change (et aussi, qu'elles ne sont pas forcément dans l'ordre attendu): |
|||
char: |
|||
&a: 3216755060 |
|||
&b: 3216755056 |
|||
a: a |
|||
b: b |
|||
int: |
|||
&a: 3216755072 |
|||
&b: 3216755076 |
|||
a: 97 |
|||
b: 98 |
|||
long double: |
|||
&a: 3216755072 |
|||
&b: 3216755084 |
|||
a: 97.000000 |
|||
b: 98.000000 |
|||
En effet, les |
|||
types considérés ont des tailles différentes. La primitive <tt>sizeof</tt> |
|||
donne (en octets) l'espace mémoire nécessaire pour stocker une donnée du type |
|||
considéré: |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
void aff_taille (const char nom[], size_t taille) { |
|||
printf("%16s : %2u\n",nom,taille) ; |
|||
} |
|||
/* Affiche les tailles de types à taille fixe les plus courants */ |
|||
int main () { |
|||
printf("Liste de tailles :\n") ; |
|||
aff_taille("void",sizeof(void)) ; |
|||
aff_taille("char",sizeof(char)) ; |
|||
aff_taille("short int",sizeof(short int)) ; |
|||
aff_taille("int",sizeof(int)) ; |
|||
aff_taille("long int",sizeof(long int)) ; |
|||
aff_taille("float",sizeof(float)) ; |
|||
aff_taille("double",sizeof(double)) ; |
|||
aff_taille("long double",sizeof(long double)) ; |
|||
aff_taille("void*",sizeof(void*)) ; |
|||
aff_taille("int*",sizeof(int*)) ; |
|||
aff_taille("long double*",sizeof(long double*)) ; |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
Sur ma machine, on obtient la sortie: |
|||
Liste de tailles : |
|||
void : 1 |
|||
char : 1 |
|||
short int : 2 |
|||
int : 4 |
|||
long int : 4 |
|||
float : 4 |
|||
double : 8 |
|||
long double : 12 |
|||
void* : 4 |
|||
int* : 4 |
|||
long double* : 4 |
|||
Noter que la taille des pointeurs est toujours la même (et c'est celle du type <tt>int</tt>). |
|||
Le type du pointeur renseigne le compilateur sur le décalage d'adresse à produire lors d'opérations arithmétiques: |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
int main () { |
|||
char *s ; |
|||
int *p ; |
|||
long double *q ; |
|||
int i ; |
|||
for (i=0;i<4;++i) { |
|||
++s ; |
|||
printf("s+%d : %u\n",i,s) ; |
|||
} |
|||
for (i=0;i<4;++i) { |
|||
++p ; |
|||
printf("p+%d : %u\n",i,p) ; |
|||
} |
|||
for (i=0;i<4;++i) { |
|||
++q ; |
|||
printf("q+%d : %u\n",i,q) ; |
|||
} |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
On observe que les écarts reflètent bien la taille du type annoncé (1, 4 et 12 ici): |
|||
s+0 : 3086073265 |
|||
s+1 : 3086073266 |
|||
s+2 : 3086073267 |
|||
s+3 : 3086073268 |
|||
p+0 : 134513757 |
|||
p+1 : 134513761 |
|||
p+2 : 134513765 |
|||
p+3 : 134513769 |
|||
q+0 : 3216681396 |
|||
q+1 : 3216681408 |
|||
q+2 : 3216681420 |
|||
q+3 : 3216681432 |
|||
Revenons sur l'allocation des tableaux avec le code suivant: |
|||
<source lang="c"> |
|||
#include <stdio.h> |
|||
#define TAILLE 1024 |
|||
int t1 [TAILLE] ; |
|||
int t2 [1] ; |
|||
int t3 [TAILLE] ; |
|||
int t4 [1] ; |
|||
void aff_point (int *p) { |
|||
printf("%u = %x -> %i\n",p,p,*p) ; |
|||
} |
|||
int * tab_nouv (int n) { |
|||
int t[TAILLE] ; |
|||
int i ; |
|||
for (i=0; i<TAILLE; ++i) |
|||
t[i]=n ; |
|||
return t ; |
|||
} |
|||
/* Affiche les adresses des tableaux statiques et de deux "tableaux" créés par |
|||
* tab_nouv() */ |
|||
int main () { |
|||
int *t5 = tab_nouv(4) ; |
|||
int *t6 = tab_nouv(2) ; |
|||
printf("Tableaux statiques:\n") ; |
|||
aff_point(t1) ; |
|||
aff_point(t2) ; |
|||
aff_point(t3) ; |
|||
aff_point(t4) ; |
|||
printf("Tableaux dynamiques (?):\n") ; |
|||
aff_point(t5) ; |
|||
aff_point(t6) ; |
|||
return 0 ; |
|||
} |
|||
</source> |
|||
L'intention de l'auteur est visiblement de retourner un tableau « frais » |
|||
à chaque appel de <tt>tab_nouv</tt>. C'est un peu raté, car la sortie de ce programme |
|||
ressemble à: |
|||
Tableaux statiques: |
|||
134522656 = 804a720 -> 0 |
|||
134518496 = 80496e0 -> 0 |
|||
134518528 = 8049700 -> 0 |
|||
134522624 = 804a700 -> 0 |
|||
Tableaux dynamiques (?): |
|||
3219749988 = bfe97c64 -> 2 |
|||
3219749988 = bfe97c64 -> 2 |
|||
Les adresses renvoyées par <tt>tab_nouv</tt> sont les mêmes et le contenu est |
|||
écrasé. C'est bien normal: ces tableaux sont alloués sur la pile, puis |
|||
immédiatement oubliés à la sortie de la fonction. |
|||
On souhaite donc faire de l'allocation dynamique: c'est le rôle de la fonction |
|||
<tt>void *malloc(size_t taille)</tt> qui renvoie l'adresse de début |
|||
d'une zone de mémoire allouée, d'une taille au moins égale à <tt>taille</tt>. |
|||
On pourra donc réécrire: |
|||
<source lang="c"> |
|||
int * tab_nouv (int n) { |
|||
int *t = malloc(TAILLE*sizeof(int)) ; |
|||
// la taille est donnée en octets ! |
|||
int i ; |
|||
for (i=0; i<TAILLE; ++i) |
|||
t[i]=n ; |
|||
return t ; |
|||
} |
|||
</source> |
|||
On peut vérifier que la nouvelle version fonctionne bien sur la sortie: |
|||
Tableaux statiques: |
|||
134522720 = 804a760 -> 0 |
|||
134518560 = 8049720 -> 0 |
|||
134518592 = 8049740 -> 0 |
|||
134522688 = 804a740 -> 0 |
|||
Tableaux dynamiques: |
|||
134529032 = 804c008 -> 4 |
|||
134533136 = 804d010 -> 2 |
|||
Notez que les adresses rendues par <tt>malloc</tt> sont proches de celles des variables statiques. |
|||
Application à la lecture de lignes de longueur non bornée: |
|||
<source lang="c"> |
|||
#include <stdlib.h> |
|||
#include <stdio.h> |
|||
==== En TP ==== |
|||
// On alloue par blocs de talle MORCEAU. |
|||
* un TP de mise en route et de précision de la notion de compilation en C |
|||
#define MORCEAU 1024 |
|||
* un projet logiciel sur les deux dernières séances (8h) |
|||
==== Outils et concepts (survol théorique et utilisation optionnelle en TP) ==== |
|||
/* Lit une ligne en entrée _quelle que soit sa taille_ |
|||
* automatisation de la compilation (make), |
|||
* et la stocke dans *p (p est un pointeur sur une chaîne). |
|||
* analyse de l'exécution et déboguage (gdb et DDD), |
|||
* Renvoie le nombre de caractère lus (-1 en cas d'erreur). */ |
|||
* boîte à outils graphique (gtk+) |
|||
=== Supports === |
|||
int lit_ligne (char **p) { |
|||
char c ; |
|||
char *s ; |
|||
int pos, blocs, i ; |
|||
* Exercices de TD : |
|||
// La position courante dans la chaîne est i+MORCEAU*blocs. |
|||
*# la [http://www.lama.univ-savoie.fr/~vaux/ens/INFO517-TD1.pdf feuille 1] ; |
|||
pos = blocs = i = 0 ; |
|||
*# la [http://www.lama.univ-savoie.fr/~vaux/ens/INFO517-TD2.pdf feuille 2] et une archive [http://www.lama.univ-savoie.fr/~vaux/ens/liste.tar.gz <tt>liste.tar.gz</tt>] comprenant une solution pour l'implémentation des listes et des listes triées (les piles ont été traitées en [[INFO517-cours7|séance 7]]). |
|||
* Devoir à la maison : le sujet [http://www.lama.univ-savoie.fr/~vaux/ens/INFO517-DM1.pdf <tt>INFO517-DM1.pdf</tt>] et les fichiers sources [http://www.lama.univ-savoie.fr/~vaux/ens/dm1.c <tt>dm1.c</tt>] et [http://www.lama.univ-savoie.fr/~vaux/ens/mat.c <tt>mat.c</tt>] associés. |
|||
// On alloue le premier morceau. |
|||
s = malloc ((++blocs)*MORCEAU*sizeof(char)) ; |
|||
if (s == NULL) return -1 ; |
|||
* Examens : |
|||
while ((c=getchar())!=EOF && c!='\n') { |
|||
*# sujet [http://www.lama.univ-savoie.fr/~vaux/ens/INFO517-Partiel1.pdf <tt>INFO517-Partiel1.pdf</tt>] et corrigé [http://www.lama.univ-savoie.fr/~vaux/ens/INFO517-Partiel1-correction.pdf <tt>INFO517-Partiel1-correction.pdf</tt>]. |
|||
s[pos] = c ; |
|||
*# sujet [http://www.lama.univ-savoie.fr/~vaux/ens/INFO517-Partiel2.pdf <tt>INFO517-Partiel2.pdf</tt>] et corrigé [http://www.lama.univ-savoie.fr/~vaux/ens/INFO517-Partiel2-correction.pdf <tt>INFO517-Partiel2-correction.pdf</tt>]. |
|||
if (i==MORCEAU-1) { // on déborderait en faisant ++i |
|||
*# sujet [http://www.lama.univ-savoie.fr/~vaux/ens/INFO517-Terminal.pdf <tt>INFO517-Terminal.pdf</tt>] et corrigé [http://www.lama.univ-savoie.fr/~vaux/ens/INFO517-Terminal-correction.pdf <tt>INFO517-Terminal-correction.pdf</tt>]. |
|||
if ((s=realloc(s,(++blocs)*MORCEAU*sizeof(char))) == NULL) { |
|||
*# sujet [http://www.lama.univ-savoie.fr/~vaux/ens/INFO517-Rattrapage.pdf <tt>INFO517-Rattrapage.pdf</tt>] et corrigé [http://www.lama.univ-savoie.fr/~vaux/ens/INFO517-Rattrapage-correction.pdf <tt>INFO517-Rattrapage-correction.pdf</tt>]. |
|||
return -1 ; // plus de mémoire : on quitte |
|||
} |
|||
i=0 ; |
|||
} else { |
|||
++i; |
|||
} |
|||
++pos ; |
|||
} |
|||
=== Séances de Cours/TD === |
|||
// si c == '\n' il faut l'ajouter |
|||
if (c=='\n') { |
|||
s[pos] = c ; |
|||
// on pourrait déborder en ajoutant le '\0': on gère ça |
|||
if (i==MORCEAU-1) { |
|||
if ((s=realloc(s,(pos+1)*sizeof(char))) == NULL) |
|||
return -1 ; // plus de mémoire : on quitte |
|||
} |
|||
++pos ; |
|||
} |
|||
# [[INFO517-cours1|lundi 22 septembre 2008]]. |
|||
s[pos]='\0' ; |
|||
#* mise en route: exemples de programmes simples et compilation; |
|||
*p=s ; |
|||
#* syntaxe de base: types, déclarations, affectations, boucles, entrées et sorties simples (caractère par caractère); |
|||
return pos ; |
|||
# [[INFO517-cours2|lundi 29 septembre 2008]] |
|||
#* fonctions; |
|||
#* tableaux et chaînes; |
|||
#* récursion; |
|||
# [[INFO517-cours3|lundi 6 octobre 2008]] |
|||
#* exercices (feuille 1); |
|||
# [[INFO517-cours4|lundi 13 octobre 2008]] |
|||
#* adresses et pointeurs; |
|||
#* passage par adresse; |
|||
#* les tableaux comme pointeurs; |
|||
#* opérateur <tt>sizeof</tt>; |
|||
#* arithmétique de pointeurs; |
|||
#* allocation des variables locales: le problème des tableaux; |
|||
#* allocation dynamique: <tt>malloc()</tt>, <tt>free()</tt>, <tt>realloc()</tt>; |
|||
# [[INFO517-cours5|lundi 20 octobre 2008]] |
|||
#* modèle mémoire: pile, tas, segment de code; |
|||
#* différence entre déclaration de tableau et déclaration de pointeur; |
|||
#* affichage des données de la pile d'exécution, adresses de retour; |
|||
# [[INFO517-cours6|lundi 3 novembre 2008]] |
|||
#* types complexes: <tt>struct</tt>, <tt>union</tt>, <tt>enum</tt>; |
|||
#* premier partiel; |
|||
# [[INFO517-cours7|lundi 10 novembre 2008]] |
|||
#* pointeurs vers <tt>struct</tt>; |
|||
#* structures récursives; |
|||
#* exemple: les piles (FILO); |
|||
#* <tt>Makefile</tt>s; |
|||
#* exercices (feuille 2); |
|||
# [[INFO517-cours8|lundi 17 novembre 2008]] |
|||
#* correction du premier partiel; |
|||
#* exercices (suite de la feuille 2); |
|||
# [[INFO517-cours9|lundi 24 novembre 2008]] |
|||
#* préparation du TP1 |
|||
#* entrées et sorties dans des fichiers |
|||
#* opérations bit-à-bit; |
|||
# [[INFO517-cours10|lundi 1er décembre 2008]] |
|||
#* deuxième partiel; |
|||
#* pointeurs sur fonctions; |
|||
#* une session DDD; |
|||
=== Séances de TP === |
|||
} |
|||
Les sujets de TP se trouvent sur |
|||
/* Lit des lignes sur l'entrée et les affiche à l'envers. */ |
|||
[http://www.lama.univ-savoie.fr/~vaux/ens/INFO-517-TP cette page]. |
|||
int main () { |
|||
char *s ; |
|||
int ret ; |
|||
while ((ret=lit_ligne(&s))>0) { |
|||
if (s[ret-1]=='\n') |
|||
ret--; |
|||
for (;ret>0;--ret) |
|||
putchar(s[ret-1]) ; |
|||
putchar('\n') ; |
|||
free(s) ; |
|||
} |
|||
return ret ; |
|||
} |
|||
</source> |
|||
La fonction <tt>void *realloc(void *p, size_t taille)</tt> demande de changer |
|||
la taille de la zone allouée par <tt>malloc</tt> pointée par <tt>p</tt>, en en |
|||
conservant le contenu. |
|||
# mercredi 15 octobre 2008 : [http://www.lama.univ-savoie.fr/~vaux/ens/INFO-517-TP/TP0.html TP0 — Préliminaires] |
|||
La fonction <tt>void free(void *p)</tt> libère la zone de mémoire allouée par |
|||
# vendredi 28 novembre 2008 : [http://www.lama.univ-savoie.fr/~vaux/ens/INFO-517-TP/TP1.html TP1 — Formats d'image] |
|||
<tt>malloc</tt> ou <tt>realloc</tt> et la rend au système: on a fini de s'en servir. |
|||
# vendredi 12 décembre 2008 : [http://www.lama.univ-savoie.fr/~vaux/ens/INFO-517-TP/TP2.html TP2 — Affichage et interface graphique] |
|||
C'est essentiel, car on alloue autant de zones que de lignes lues: sans l'appel à <tt>free</tt> en fin de boucle, |
|||
on conserverait en mémoire toutes les lignes alors que seule la dernière nous intéresse. |
|||
==Références== |
==Références== |
Dernière version du 17 février 2012 à 07:59
Cours du semestre 5 de la licence STIC INFO.
- Responsable pour 2010--2011: Jacques-Olivier Lachaud
- Jacques-Olivier Lachaud (C/TD/TP), Xavier Provençal (TP)
Pensez à consulter les indications pour compiler un petit programme sur une machine des salles de TP.
Quelques ressources pour l'étudiant (2010-2011)
- Notes de cours PostScript PDF
- Fiches de TD
- TD 1 et 2 : tableaux, entrées-sorties PostScript PDF
- TD 3 : exercices sur les pointeurs PostScript PDF
- TPs et autres travaux pratiques Pages des TPs
- Pour la première fois, on pourra aussi regarder la page Comment_compiler_le_C_?
- Si vous n'accédez pas aux pages "manual" en salle TP, on les trouve en ligne : [Manual pages]
- Autres ressources
N'hésitez pas à contribuer au wiki, et en particulier à cette page: clarifications, compléments, exemples… Si vous n'avez pas compris un point particulier, vous pouvez signaler votre problème sur la page de discussion (onglet en haut de cette page) ou par les moyens habituels. Il sera ensuite très positif de revenir sur cette page et de consigner ce qui vous posait problème et ce qui vous a permis de mieux comprendre.
Déroulement (2010-2011)
Ceci n'est qu'une prévision.
- (Cours 1): lundi 20 septembre. Langage C, intérêts et défauts. Compilation. Eléments de base du langage (types simples, variables, expressions). (=> I.5).
- (TD 1): mercredi 29 septembre. Instructions et structures de contrôle usuelles (conditionnelles, boucles) (=> I.10)
- (TD 2): jeudi 30 septembre. TD 1 tableaux, fonctions en C, E/S simples.
- (Cours 2): vendredi 1er octobre. Fonctions, passage de paramètres, pointeurs (=> II.5).
- (TD 3): vendredi 1er octobre. TD 1 tableaux, fonctions en C, E/S simples (II).
- (Cours 3): lundi 4 octobre. Fonctions, passage de paramètres, pointeurs, allocation dynamique (=> II.10)
- (Cours 4): lundi 4 octobre. Un exemple complet : les piles en C (II.11).
- (TP 1): mercredi 6 octobre. Boucles, puissance 4, tracés avec gnuplot, récursivité.
- (TD 4): vendredi 8 octobre. Exercices simples sur les pointeurs.
- (TD 5): lundi 11 octobre. Pile d'exécution. Structures auto-référents. Début skip-liste.
- (TP 2): mercredi 13 octobre. Tetris texte.
- (TD 6): vendredi 15 octobre. Skip-listes. Compilation séparée et bonnes habitudes de développement C.
- (TP 3): jeudi 21 octobre. Tetris graphique avec GTK.
Historique
- Responsable pour 2009--2010: Emilie Charrier (C/TD/TP)
- Responsable pour 2008--2009: Lionel Vaux (C/TD/TP)
Ressources pour l'étudiant (avant 2010)
Cet enseignement comprendra 10 séances de cours/TD (1h30) et 3 séances de TP (4h).
La distinction entre cours et TD restera floue. Je vous demanderai généralement d'écrire quelques petits programmes d'une semaine sur l'autre. Autant que possible, envoyez-moi vos fichiers sources à l'adresse lionel.vaux@univ-savoie.fr, afin que je puisse évaluer le niveau de chacun et ajuster le contenu des séances suivantes.
Et dites-moi si ça ne va pas, ou je risque d'avancer trop vite.
Objectifs du cours
Cours/TD
- Principes généraux et particularités du langage: programmation impérative, typage fort à la compilation, adressage mémoire explicite
- Syntaxe de base
- Bibliothèque standard: entrée-sorties et interaction avec le système d'exploitation
- C avancé:
- allocation dynamique
- modèle mémoire (pile, tas, code)
- pointeurs sur structures
- pointeurs sur fonctions
- Bonnes pratiques
En TP
- un TP de mise en route et de précision de la notion de compilation en C
- un projet logiciel sur les deux dernières séances (8h)
Outils et concepts (survol théorique et utilisation optionnelle en TP)
- automatisation de la compilation (make),
- analyse de l'exécution et déboguage (gdb et DDD),
- boîte à outils graphique (gtk+)
Supports
- Exercices de TD :
- la feuille 1 ;
- la feuille 2 et une archive liste.tar.gz comprenant une solution pour l'implémentation des listes et des listes triées (les piles ont été traitées en séance 7).
- Devoir à la maison : le sujet INFO517-DM1.pdf et les fichiers sources dm1.c et mat.c associés.
- Examens :
- sujet INFO517-Partiel1.pdf et corrigé INFO517-Partiel1-correction.pdf.
- sujet INFO517-Partiel2.pdf et corrigé INFO517-Partiel2-correction.pdf.
- sujet INFO517-Terminal.pdf et corrigé INFO517-Terminal-correction.pdf.
- sujet INFO517-Rattrapage.pdf et corrigé INFO517-Rattrapage-correction.pdf.
Séances de Cours/TD
- lundi 22 septembre 2008.
- mise en route: exemples de programmes simples et compilation;
- syntaxe de base: types, déclarations, affectations, boucles, entrées et sorties simples (caractère par caractère);
- lundi 29 septembre 2008
- fonctions;
- tableaux et chaînes;
- récursion;
- lundi 6 octobre 2008
- exercices (feuille 1);
- lundi 13 octobre 2008
- adresses et pointeurs;
- passage par adresse;
- les tableaux comme pointeurs;
- opérateur sizeof;
- arithmétique de pointeurs;
- allocation des variables locales: le problème des tableaux;
- allocation dynamique: malloc(), free(), realloc();
- lundi 20 octobre 2008
- modèle mémoire: pile, tas, segment de code;
- différence entre déclaration de tableau et déclaration de pointeur;
- affichage des données de la pile d'exécution, adresses de retour;
- lundi 3 novembre 2008
- types complexes: struct, union, enum;
- premier partiel;
- lundi 10 novembre 2008
- pointeurs vers struct;
- structures récursives;
- exemple: les piles (FILO);
- Makefiles;
- exercices (feuille 2);
- lundi 17 novembre 2008
- correction du premier partiel;
- exercices (suite de la feuille 2);
- lundi 24 novembre 2008
- préparation du TP1
- entrées et sorties dans des fichiers
- opérations bit-à-bit;
- lundi 1er décembre 2008
- deuxième partiel;
- pointeurs sur fonctions;
- une session DDD;
Séances de TP
Les sujets de TP se trouvent sur cette page.
- mercredi 15 octobre 2008 : TP0 — Préliminaires
- vendredi 28 novembre 2008 : TP1 — Formats d'image
- vendredi 12 décembre 2008 : TP2 — Affichage et interface graphique
Références
- The C programming language, de Kernighan et Ritchie;
- Le langage C, version française du précédent;
- Le polycopié de Bernard Cassagne, disponible ici, au format html (consultable en ligne) ou pdf;
- Le wikilivre Programmation C: un livre de cours sur le mode wikipedia.