# Exercices en langage C/Notions de base

* Préparation
* Notions de base
* Les variables et constantes
* Les types
* Les opérateurs
* Les structures de contrôle
* Les tableaux statiques
* Les fonctions
* Les chaînes de caractères
* Les pointeurs
* Le préprocesseur C
* Préparation
* Notions de base
* Les variables et constantes
* Les types
* Les opérateurs
* Les structures de contrôle
* Les tableaux statiques
* Les fonctions
* Les chaînes de caractères
* Les pointeurs
* Le préprocesseur C
[ Modifier le sommaire ]
Ces exercices sur les notions de base abordent :
* définition de fonction
* la fonction main
* l'appel de fonction
* la bibliothèques standard, l'inclusion avec le préprocesseur
* la sortie formatée avec printf
* les paramètres de main

### Exercice 1 : programme minimal nop

Écrire un programme qui ne fait rien (nop signifie no operation, soit « aucune opération »).
Pour le compiler et l'exécuter :
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 main servant de point d'entrée
* valeur retournée par une fonction
Un programme C commence par l'exécution d'une fonction main. Il existe deux prototypes standards pour la fonction main : int main(void) et int main(int argc, char* argv). Nous utilisons le plus simple car nous n'avons pas besoin de argc ni argv. Remarquons que les deux prototypes indiquent que main retourne un entier de type int. Nous retournons 0 (zéro), valeur conventionnelle pour indiquer que le programme est terminé sans erreur. Voici notre définition de la fonction main :
Nous pouvons aussi utiliser pour le code retour la constante EXIT_SUCCESS de stdlib.h plus parlante que 0.

### 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 true retourne systématiquement le code de succès 0.
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.
Écrire la fonction main d'un programme faux qui appelle la fonction un suivante et retourne sa valeur à la sortie :
La fonction a appeler :
Pour compiler et exécuter :
Notions :
* déclaration de fonction
* appel de fonction
* prototype de fonction
On définit la fonction un avant la fonction main. Dans main, à l'aide de l'opérateur d'appel de fonction, on procède à l'appel de fonction un, dont on retourne immédiatement la valeur.
Remarques qualité :
* 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 faudrait utiliser le mot clé static. static int un(void).
* Que se passe-t-il si l'on définit la fonction un après main ? En compilant main, le compilateur va remarquer que l'on appelle une fonction un 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 un avait été déclarée par : extern int un. 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 int un(void) et le programme suivant fonctionne. Il vaut mieux éviter ces mécanismes hasardeux, en demandant au compilateur d'être strict et en utilisant l'option -Wall.
* Ce qui donnera à la compilation
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.

### Exercice 3 : programme hello, world

Écrire un programme qui affiche hello, world.
Notions :
* inclusion d'en-tête standard
* passage de paramètres de fonction
* printf
* chaîne de caractères statique
On utilise le préprocesseur pour inclure stdio.h car le prototype de printf se trouve dans stdio.h. On passe la chaîne de caractères "hello, world\n" à l'appel de printf. On remarque que la chaîne est terminée par un caractère de nouvelle ligne (\n). 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.
Remarques
* 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 #include <stdio.h> ? Le compilateur va remarquer que l'on appelle une fonction printf dont il ne sait rien. Cela ne l'empêche pas de compiler le programme ! Le compilateur fait comme si printf avait été déclarée par : extern int printf. 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 :
L'option de gcc -Wall signale ce problème à la compilation par ce message :
Pour détecter le problème, c'est-à-dire que l'entier 123 (de type int) est passé au lieu d'une chaîne de caractères (de type const char*), le compilateur doit voir le prototype de printf, qui est : int printf(const char*, ...). Ce qui nous intéresse avec le #include <stdio.h>, c'est que le préprocesseur génère la sortie suivante en entrée du compilateur :
Notons que certains compilateurs ont une connaissance implicite des fonctions très communes comme printf. Ainsi, même sans voir le prototype de printf, certaines versions de GCC génèrent un avertissement, à moins d'utiliser la commande de compilation suivante : gcc -fno-builtin -o hello.exe hello.c. L'option -Wall active l'option --fno-builtin.

### 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).
Compilation et exécutions attendues :
Notions :
* paramètres de main
* sortie formatée
Pour avoir accès aux arguments en ligne de commande, on utilise la seconde définition standard de main, qui a pour premier paramètre le nombre d'arguments argc, et pour second paramètre le vecteur d'arguments argv. Notons que sur Unix, le nom de l'exécutable lui-même (./arg) est un argument, donc argc vaut au moins 1. Le résultat recherché est argc - 1. Pour afficher un nombre entier, nous utilisons des caractères de conversion dans la chaîne de formatage passée à printf. Le caractère de conversion d suivant le caractère % indique que nous affichons un entier de type int.