« Exercices en langage C/Notions de base » : différence entre les versions
Contenu supprimé Contenu ajouté
Compléments qualité |
|||
Ligne 12 :
Écrire un programme qui ne fait rien (''nop'' signifie ''no operation'', soit « aucune opération »).
Pour le compiler et l'exécuter :
<pre>
>
> ./nop.exe
>
</pre>
'''Remarque qualité''' :
* L'option '''-Wall''' n'est pas obligatoire. Elle demande au compilateur de signaler toutes les fautes qu'il peut détecter. Cela produit quelquefois des ''warnings'' ou avertissement que le programmeur, désireux de produire un code de qualité, s'efforcera de comprendre la signification et de corriger.
* Je donne l'extension .exe à l'exécutable produit pour l'identifier plus facilement. Sous Windows l'extension .exe est obligatoire et rajoutée par l'éditeur de lien si oubli.
Notions :
*définition de fonction
*fonction principale <code>main</code> servant de poit d'entrée
*valeur retournée par une fonction
Ligne 27 ⟶ 33 :
<source lang="C">
/*
int main(void)
{
Ligne 34 ⟶ 40 :
</source>
Nous pouvons aussi utiliser pour le code retour la constante '''EXIT_SUCCESS''' de ''stdlib.h'' plus parlante que 0.
<source lang="C">
#include <stdlib.h>
</source>▼
{
return EXIT_SUCCESS;
<source lang="C">▼
}
▲void main(void) {}
</source>
}}
=== Exercice 2 : programme ''false'' ===
Dans les faits, le programme précédent retourne toujours le code de succès 0 ; il correspond donc à la commande Unix ''true''. La commande ''
<pre>
MacMini-TM:~ thierry$ true
MacMini-TM:~ thierry$ echo $?
0
</pre>
Pour écrire ''faux'' (équivalent du programme UNIX ''false''), cet exercice introduit une complication : l'appel d'une fonction contenue dans le même fichier source que la fonction principale main.
La fonction a appeler :
<source lang="c">
int un(void)
Ligne 63 ⟶ 72 :
</source>
Pour compiler et exécuter :
<pre>
>
> ./faux.exe
> echo $?
1
</pre>
Notions :
*déclaration de fonction
*appel de fonction▼
*prototype de fonction
▲*appel de fonction
{{Boîte déroulante|titre=Solution|contenu =
Ligne 79 ⟶ 89 :
<source lang="c">
int un(void)
{
Ligne 91 ⟶ 100 :
</source>
Remarques qualité :
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 un(void)</code> et le programme suivant fonctionne :▼
* Lorsque on définit une fonction de la sorte, elle est par défaut '''extern''' (visible de l'extérieur). Dans un projet plus important, elle pourrait donc être appelée à la place d'une autre portant le même nom. Pour limiter sa visibilité en interne, il faudait utiliser le mot clé '''static'''. <code>static int un(void)</code>.
▲* 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 ! ('''sans l'option -Wall il ne signalera rien !''') 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 un(void)</code> et le programme suivant fonctionne.
<source lang="c">
/* Réponse
int main(void)
{
Ligne 105 ⟶ 115 :
}
</source>
*Ce qui donnera à la compilation
<pre>
MacMini-TM:~/Documents/developpement/c thierry$ gcc -o faux.exe faux.c
MacMini-TM:~/Documents/developpement/c thierry$ gcc -Wall -o faux.exe faux.c
faux.c: In function 'main':
faux.c:4: warning: implicit declaration of function 'un'
</pre>
Si l'on souhaite tout de même mettre la fonction ''main'' en premier, il aurait fallu avertir le compilateur en utilisant un prototype : la signature de la fonction, placé avant le ''main''.
static int un(void);
int main(void)
{
return un();
}
static int un(void)
{
return 1;
}
▲</source>
}}
Ligne 112 ⟶ 143 :
<pre>
>
> ./hello.exe
hello, world
>
Ligne 133 ⟶ 164 :
int main(void)
{
(void)printf("hello, world\n");
return 0;
}
</source>
'''Remarques'''
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''' :▼
* la fonction printf() retourne le nombre de caractère écrits dans le flux de sortie, ici j'ai décidé de ne pas utiliser ce code retour, je le signale en castant à void la fonction. L'outil qualité lint signale toute fonction retournant une valeur qui n'est pas affectée à une variable ou ignoré explicitement. Cela permet d'éviter l'erreur qui consiste à oublier de traiter un code retour de fonction.
▲* 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
Ligne 149 ⟶ 181 :
}
</source>
L'option de gcc -Wall signale ce problème à la compilation par ce message :
<pre>
hello.c:3: warning: implicit declaration of function 'printf'
</pre>
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 :
Ligne 168 ⟶ 205 :
</source>
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 -fno-builtin -o hello.
</ }}
Ligne 174 ⟶ 212 :
=== 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
Compilation et exécutions attendues :
<pre>
>
> ./arg.exe
0
> ./arg.exe foo bar
2
</pre>
Ligne 190 ⟶ 228 :
{{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>
<source lang="c">
/* Bonne réponse */
#include <stdio.h>
#include <stdlib.h
int main(int argc, char *argv[])
{
(void)printf("%
return
}
</source>
|