« Exercices en langage C/Notions de base » : différence entre les versions

Contenu supprimé Contenu ajouté
→‎Exercice 1 : aire et périmètre : suppr réponse bizarre et fausse
exercices beaucoup plus progressifs, n'utilisant pas des horreurs à la scanf
Ligne 1 :
{{Exercices en langage C}}
 
Ces exercices sur les '''notions de base''' aborderontabordent la saisie et traitement de différents types de données, des calculs simple jusqu'à l'utilisation de la bibliothèque mathématiques.:
* définition de fonction
* la fonction <code>main</code>
* l'appel de fonction
* la bibliothèques standard, l'inclusion avec le préprocesseur
* la sortie formattée avec <code>printf</code>
* les paramètres de <code>main</code>
 
=== Exercice 1 : programme minimal ''nop'' ===
{{bandeau d'alerte
|style=background: #ffd; width: 100%;>
|image=Achtung.svg|Avertissement
|texte='''Avertissement'''<br/>
Les codes sources présentés sont à vocation pédagogiques ne correspond pas à de bonnes pratiques de programmation notamment en sécurité. L'utilisation de ces programmes compilés peuvent entrainer des [[w:Dépassement de tampon|dépassements de capacité]].
}}
 
Écrire un programme qui ne fait rien (''nop'' signifie ''no operation'', soit « aucune opération »).
Ainsi pour tous ces exercices, il est préférable lorsque l'utilisateur saisira des données, d'utiliser la fonction <tt>lire_ligne</tt> fournie dans le livre [[Programmation C]] au chapitre : [[Programmation C Entrées/sorties#Entrées non formatées|Entrées non formatées]] :
<pre>
* on ne traitera pas les cas d'erreur où <tt>errno</tt> est placé à une valeur non nulle;
> cc nop.c -o nop
* on n'utilisera pas de compilation séparée pour <tt>lire_ligne</tt>, mais on copiera directement son code dans le fichier contenant la fonction <tt>main</tt>.
> ./nop
>
</pre>
 
Notions :
=== Exercice 1 : aire et périmètre===
*définition de fonction
Écrire un programme qui demande à l'utilisateur de taper la largeur et la longueur d'un champ rectangulaire et qui en affiche le périmètre et la surface.
*fonction <code>main</code>
<div style="width:70%">{{Boîte déroulante|titre=Solution|contenu =
*valeur retournée par une fonction
<source lang="C">
#include <stdio.h>
 
{{Boîte déroulante|titre=Solution|contenu =
int main(int argc, char *argv[])
Le standard indique qu'un programme C commence par l'exécution d'une fonction <code>main</code> qui retourne une valeur de type <code>int</code> (nombre entier). Nous devons donc définir <code>main</code>. De <code>main</code>, nous retournons 0 (zéro), valeur conventionnel pour indiquer que le programme est terminé sans erreur :
 
<source lang="C">
/* Bonne réponse */
int main(void)
{
return 0;
double largeur, longueur, surface, perimetre;
}
</source>
 
On trouve des exemples – à commencer par le ''hello world'' de Kernighan et Ritchie – où aucune valeur n'est retournée. Ce n'est pas très dangeureux, mais pas très propre. On trouve des exemples plus anciens (en K&R C, le C des années 1970) où le type <code>int</code> n'est pas déclaré, car il était implicite en K&R C, et le type <code>void</code> n'existait pas encore. '''Avec un compilateur supportant le K&R C''', on peut donc écrire le programme minimal suivant :
printf("Tapez la largeur du champ : ");
scanf("%lf", &largeur);
printf("Tapez la longueur du champ : ");
scanf("%lf", &longueur);
 
<source lang="C">
surface = largeur * longueur;
/* Réponse correcte en K&R C */
perimetre = 2 * (largeur + longueur);
main(){}
</source>
 
Plus communément, on trouve des exemples où <code>main</code> est défini comme retournant <code>void</code>. Certains compilateurs supportent cette extension, mais le '''support de <code>void main(void)</code> n'est pas garanti standard''', bien qu'il ne soit pas non plus interdit :
printf("La surface vaut : %lf\n", surface);
printf("Le perimetre vaut : %lf\n", perimetre);
 
<source lang="C">
return 0;
/* Réponse non garantie par le standard */
}</source>
void main(void) {}
</source>
 
}}</div>
 
=== Exercice 2 : moyenneprogramme ''false'' ===
Écrire un programme qui demande à l'utilisateur de taper 5 entiers et qui affiche leur moyenne. Le programme ne devra utiliser que 2 variables.
<div style="width:70%">{{Boîte déroulante|titre=Solution|contenu =
Cet exercice a pour but de vérifier les points techniques suivants :
*La notion de variables et leur déclaration.
*Calcul du moyenne.
*Utilisation des types <code>int</code> et <code>double</code>.
*L'utilisation des fonctions <code>printf</code> et <code>scanf</code>.
*L'affectation.
Voici le fichier source :
<source lang="c">
#include <stdio.h>
 
Dans les faits, le programme précédent retourne toujours le code de succès 0, il correspond à la commande Unix ''true''. La commande ''false'' retourne systématiquement le code d'échec 1. Pour écrire ''false'', on introduit une complication. Soit la fonction <code>un</code> :
int main(int argc, char *argv[])
 
<source lang="c">
int un(void)
{
return 1;
int a; /* variable temporaire pour la lecture au clavier */
}
double s = 0;
</source>
 
Écrire un programme qui utilise cette fonction et retourne sa valeur à la sortie.
printf("Tapez la valeur numero 1 : ");
scanf("%d", &a);
s = s + a;
 
<pre>
printf("Tapez la valeur numero 2 : ");
> cc false.c -o false
scanf("%d", &a);
> ./false || echo OK
s = s + a;
OK
>
</pre>
 
Notions :
printf("Tapez la valeur numero 3 : ");
*déclaration de fonction
scanf("%d", &a);
*prototype de fonction
s = s + a;
*appel de fonction
 
{{Boîte déroulante|titre=Solution|contenu =
printf("Tapez la valeur numero 4 : ");
On définit la fonction <code>un</code> avant la fonction <code>main</code>. Dans <code>main</code>, à l'aide de l'opérateur d'appel de fonction <code>()</code>, on procède à l'appel de fonction <code>un()</code>, dont on retourne immédiatement la valeur.
scanf("%d", &a);
s = s + a;
 
<source lang="c">
printf("Tapez la valeur numero 5 : ");
/* Bonne réponse */
scanf("%d", &a);
int un(void)
s = s + a;
{
return 1;
}
 
int main(void)
s = s / 5.0;
{
printf("La moyenne vaut : %lf\n", s);
return un();
}
</source>
 
Que se passe-t-il si l'on définit la fonction <code>un</code> après <code>main</code> ? En compilant <code>main</code>, le compilateur va remarquer que l'on appelle une fonction <code>un</code> dont il ne sait rien. Cela ne l'empêche pas de compiler le programme ! Le compilateur fait comme si <code>un</code> avait été déclarée par : <code>extern int un()</code>. Il y a déclaration implicite de fonction, alors que le compilateur ne sait rien de l'implémentation réelle de la fonction ! Par chance, la déclaration explicite correspond au prototype <code>int main(void)</code> et le programme suivant fonctionne :
return 0;
}</source>
}}</div>
 
<source lang="c">
=== Exercice 3 : échange entre variables ===
/* Réponse inélégante */
Écrire un programme qui demande à l'utilisateur de saisir deux entiers A et B, qui échange le contenu des variables A et B puis qui affiche A et B.
int main(void)
<div style="width:70%">{{Boîte déroulante|titre=Solution|contenu =
{
Cet exercice a pour but de vérifier les points techniques suivants :
return un();
*La notion de variables et leur déclaration.
}
*L'utilisation des fonctions <code>printf</code> et <code>scanf</code>.
*L'affectation.
*Un « algorithme » rudimentaire : échanger le contenu de 2 variables.
Voici le fichier source :
<source lang="c">#include <stdio.h>
 
int un(void)
int main(int argc, char *argv[])
{
return 1;
double a, b, temp;
}
</source>
 
}}
printf("Tapez la valeur de a : ");
scanf("%lf", &a);
printf("Tapez la valeur de b : ");
scanf("%lf", &b);
 
=== Exercice 3 : programme ''hello, world'' ===
temp = a;
Écrire un programme qui affiche ''hello, world''.
a = b;
b = temp;
 
<pre>
printf("La valeur de a est %lf\n", a);
> cc hello.c -o hello
printf("La valeur de b est %lf\n", b);
> ./hello
hello, world
>
</pre>
 
Notions :
*inclusion d'en-tête standard
*passage de paramètres de fonction
*<code>printf</code>
*chaîne de caractères statique
 
{{Boîte déroulante|titre=Solution|contenu =
On utilise le préprocesseur pour inclure <code>stdio.h</code> car le prototype de <code>printf</code> se trouve dans <code>stdio.h</code>. On passe la chaîne de caractères <code>"hello, world\n"</code> à l'appel de <code>printf</code>. On remarque que la chaîne est terminée par un caractère de nouvelle ligne (<code>\n</code>). Sans cela, une fois le programme terminé, l'invite de commande risque d'être affichée à la suite du mot ''world'' au lieu de se trouver au début d'une nouvelle ligne.
 
<source lang="c">
/* Bonne réponse */
#include <stdio.h>
 
int main(void)
{
printf("hello, world\n");
return 0;
}
}</source>
}}</divsource>
 
Que se passe-t-il si l'on omet le <code>#include <stdio.h></code> ? Le compilateur va remarquer que l'on appelle une fonction <code>printf</code> dont il ne sait rien. Cela ne l'empêche pas de compiler le programme ! Le compilateur fait comme si <code>printf</code> avait été déclarée par : <code>extern int printf()</code>. Il y a déclaration implicite de fonction, alors que le compilateur ne sait rien de l'implémentation réelle de la fonction ! Si l'on omet d'inclure les prototypes des fonctions appelées, on risque donc d'appeler ces fonctions avec des '''paramètres incompatibles sans avertissement du compilateur''' :
=== Exercice 4 : calcul de prix ===
Écrire un programme qui demande à l'utilisateur de taper le prix HT qu'un kilo de tomates, le nombre de kilos de tomates achetés, le taux de TVA (Exemple 5,5, 19,6, ...). Le programme affiche alors le prix TTC des marchandises.
<div style="width:70%">{{Boîte déroulante|titre=Solution|contenu =
Cet exercice a pour but de vérifier les points techniques suivants :
*La notion de variables et leur déclaration.
*Le choix d'identificateurs pertinents et explicites.
*L'utilisation des fonctions <code>printf</code> et <code>scanf</code>.
*L'affectation.
*Modélisation d'un problème « économique ».
Voici le fichier source :
<source lang="c">#include <stdio.h>
 
<source lang="C">
int main(int argc, char *argv[])
/* Appel de printf aberrant risquant de compiler sans avertissement
faute d'avoir inclu stdio.h. Un crash à l'exécution est probable. */
int main(void)
{
printf(123);
double prixht, poids, tva, total;
return 0;
}
</source>
 
Pour détecter le problème, c'est-à-dire que l'entier <code>123</code> (de type <code>int</code>) est passé au lieu d'une chaîne de caractères (de type <code>const char*</code>), le compilateur doit voir le prototype de <code>printf</code>, qui est : <code>int printf(const char*, ...)</code>. Ce qui nous intéresse avec le <code>#include <stdio.h></code>, c'est que le préprocesseur génère la sortie suivante en entrée du compilateur :
printf("Tapez le prix HT d'un kilo de tomates : ");
scanf("%lf", &prixht);
printf("Combien de kilos avez-vous achetes : ");
scanf("%lf", &poids);
printf("Quel est le taux de TVA : ");
scanf("%lf", &tva);
 
<source lang="C">
total = (1 + tva / 100) * prixht * poids;
/* Ce que le compilateur voit */
/* Contenu de stdio.h... */
 
/* Prototype de printf */
printf("Le prix TTC est : %lf\n", total);
int printf(const char *, ...);
 
/* ... fin de stdio.h */
 
int main(void)
{
printf("hello, world\n");
return 0;
}
}</source>
}}</divsource>
 
Notons que certains compilateurs ont une connaissance implicite des fonctions très communes comme <code>printf</code>. Ainsi, même sans voir le prototype de <code>printf</code>, certaines versions de GCC génèrent un avertissement, à moins d'utiliser la commande de compilation suivante : <code>gcc hello.c .o hello -fno-builtin</code>.
=== Exercice 5 : distance entre deux points ===
Écrire un programme qui demande à l'utilisateur de saisir les coordonnées de deux points du plan A et B et qui affiche la distance entre A et B.<br/>
Indication 1 : on pourra utiliser le théorème de Pythagore.<br/>
Indication 2 : dans le fichier d'en-tête <code>math.h<code>, il y a une fonction <code>sqrt</code> qui calcule la racine carrée.<br/>
Exemple d'utilisation : <code>x = sqrt(y);</code><br/>
x et y doivent être des double.<br/></pre>
 
}}
<div style="width:70%">{{Boîte déroulante|titre=Solution|contenu =
Cet exercice a pour but de vérifier les points techniques suivants :
*La notion de variables et leur déclaration.
*L'utilisation des fonctions <code>printf</code> et <code>scanf</code>.
*L'affectation.
*Utilisation de variables explicites.
*Utilisation de la bibliothèque <code>math.h</code>.
*Modélisation d'un problème issu de la géométrie sous forme informatique.
Voici le fichier source :
<source lang="c">#include <stdio.h>
#include <math.h>
 
=== Exercice 4 : programme comptant les ''arguments'' en ligne de commande'' ===
int main(int argc, char *argv[])
{
double XA, YA, XB, YB, dx, dy, distance;
 
Écrire un programme qui affiche le nombre de paramètres en ligne de commande (en anglais, les ''arguments'' sont les données passées au programme, ou à une fonction).
printf("Tapez l'abscisse de A : ");
scanf("%lf", &XA);
printf("Tapez l'ordonnée de A : ");
scanf("%lf", &YA);
printf("Tapez l'abscisse de B : ");
scanf("%lf", &XB);
printf("Tapez l'ordonnée de B : ");
scanf("%lf", &YB);
 
<pre>
dx = XA - XB;
> cc dy = YAarg.c -o YB;arg
> ./arg
distance = sqrt(dx * dx + dy * dy);
0
> ./arg foo bar
2
>
</pre>
 
Notions :
printf("La distance AB est : %lf\n", distance);
*paramètres de <code>main</code>
*sortie formattée
 
{{Boîte déroulante|titre=Solution|contenu =
Pour avoir accès aux arguments en ligne de commande, on utilise la seconde définition standard de <code>main</code>, qui a pour premier paramètre le nombre d'arguments <code>argc</code>, et pour second paramètre le vecteur d'arguments <code>argv</code>. Notons que sur Unix, le nom de l'exécutable lui-même (<code>./arg</code>) est un argument, donc <code>argc</code> vaut au moins 1. Le résultat recherché est <code>argc - 1</code>. Pour afficher un nombre entier, nous utilisons des caractères de conversion dans la chaîne de formattage passée à <code>printf</code>. Le caractère de conversion <code>i</code> suivant le caractère <code>%</code> indique que nous affichons un entier de type <code>int</code>.
 
<source lang="c">
/* Bonne réponse */
#include <stdio.h>
 
int main(int argc, char *argv[])
{
printf("%i\n", argc - 1);
return 0;
}
}</source>
}}</divsource>
 
}}
''N'oubliez pas de lier la compilation avec la bibliothèque math'' :
$ cc exo5.c -o exo5 -lm
 
[[Catégorie:Exercices en langage C (livre)]]