« 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'''
* 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'' ===
Écrire un programme qui ne fait rien (''nop'' signifie ''no operation'', soit « aucune opération »).
<pre>
> cc nop.c -o nop
> ./nop
>
</pre>
Notions :
*définition de fonction
*fonction <code>main</code>
*valeur retournée par une fonction
{{Boîte déroulante|titre=Solution|contenu =
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;
}
</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 :
<source lang="C">
/* Réponse correcte en K&R C */
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 :
<source lang="C">
/* Réponse non garantie par le standard */
void main(void) {}
</source>
}}
=== Exercice 2 :
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> :
<source lang="c">
int un(void)
{
return 1;
}
</source>
Écrire un programme qui utilise cette fonction et retourne sa valeur à la sortie.
<pre>
> cc false.c -o false
> ./false || echo OK
OK
>
</pre>
Notions :
*déclaration de fonction
*prototype de fonction
*appel de fonction
{{Boîte déroulante|titre=Solution|contenu =
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.
<source lang="c">
/* Bonne réponse */
int un(void)
{
return 1;
}
int main(void)
{
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 :
<source lang="c">
/* Réponse inélégante */
int main(void)
{
return un();
}
int un(void)
{
return 1;
}
</source>
}}
=== Exercice 3 : programme ''hello, world'' ===
Écrire un programme qui affiche ''hello, world''.
<pre>
> cc hello.c -o hello
> ./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;
}
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''' :
<source lang="C">
/* 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);
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 :
<source lang="C">
/* Ce que le compilateur voit */
/* Contenu de stdio.h... */
/* Prototype de printf */
int printf(const char *, ...);
/* ... fin de stdio.h */
int main(void)
{
printf("hello, world\n");
return 0;
}
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 4 : programme comptant les ''arguments'' en ligne de commande'' ===
É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).
<pre>
> cc
> ./arg
0
> ./arg foo bar
2
>
</pre>
Notions :
*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;
}
}}
[[Catégorie:Exercices en langage C (livre)]]
|