
Programmation : Initiation au C

Pour ce numéro, nous allons voir, en première partie la compilation séparée, les différentes classe de variables et de fonctions en C, ainsi que la
struture générale d'un programme; puis en seconde partie les entrées/sorties en C. Mais commençons par la structure d'un programme :
Struture d'un programme
La struture d'un module ou d'un programme en C est à peu prêt la même. Ainsi l'on commence par faire les inclusions (les célèbres #include <> ou
"") de fichiers nécessaire au module. Ces inclusions permettent de définir des macro-définitions, des types, des prototypages de fonctions externes, des variables
externes... Ces inclusions sont obligatoires (ou chaudement recommandée) car elles permettent au compilateur C, de savoir quelle est le type de telles ou telles fonctions, de savoir que
cette fonction est une pseudo-fonction et pas une fonction... Ensuite il y a La déclaration des constantes, des pseudo-fonctions... Puis la déclaration des variables
globales. Ces variables sont visibles partout dans le module, alors attention aux modifications intempestives (source de nombreux bugs). Après les variables globales, viennent les
prototypages de fonctions internes ou externes, les fonctions proprement dites. Ce qui nous donnent comme schémas général :
- Ensemble des inclusions : #include
- Ensemble des macros-définitions : #define
- Ensemble des variables globales
- Prototypage des fonctions
- Déclaration des fonctions
Classe de variables/fonctions
La classe de variable (ou de fonction), est ce qui détermine la portée et la durée de vie d'une variable (ou d'une fonction). Vous connaissez tous les variables locales,
ces variables ont comme portée, l'ensemble de la fonction, et comme durée de vie l'exécution de la fonction (elles sont en fait allouée dynamiquement sur la
pile, lors de l'appelle de la fonction). Mais il est possible de modifié se comportement, grâce au classe. Donc pour les variables locales, nous avons :
- auto :
- c'est la classe par défaut. la variable à pour durée de vie, l'exécution de la fonction, puis est détruite. Elle n'est visible que dans la fonction
- register :
- la variable est stockée dans un registre, il est alors impossible d'avoir son adresse.
- static :
- ce type de variables est un peu particulier. Ainsi leurs contenus est conservé d'un appel à l'autre. En fait elles sont stockées au même endroit que les
variables globales, sauf quelles ne sont visibles que dans la fonctions.
- extern :
- la variable qui est de cette classe n'appartient pas à la fonction elle est donc externe à la fonction. C'est comme ça que l'on devrait définir
l'utilisation des variables globales dans une fonction, ou des variables externes au module qui ne doivent être visible que dans certaine fonction.
- const :
- cette variable est constante! Le compilateur sait que cette variable ne peut être modifiée et peut donc optimisé en conséquence.
- volatile :
- cette variable peut changer de contenu sans que le programme ne s'en aperçoive. C'est le cas des registres d'entrées/sortie sur un 680X0, des variables du GEM...
Et pour les variables globales, nous avons :
- aucune classe :
- la variable est visible dans tout le module (comme pour toutes les variables globales). Elle peut être exportée dans un autre module. Sa durée de vie est
l'exécution du programme.
- static :
- cette variable ne peut pas être exportée, elle serat donc interne au module.
- extern :
- cette variable est définie dans un autre module (pas en static!), et peut être utilisée dans ce module.
Les classes des fonctions sont les mêmes que les classes de variables globales, avec les mêmes propriétées. Pour réaliser une compilation
séparée, rien de plus simple, il suffit déclarer au gestionnaire de projet, les différents modules utilisés dans le programme. ce gestionnaire de projet
prend la forme soit d'un fichier make pour le Sozobon ou le Gcc, soit d'un fichier prj pour le Pure C. Quoiqu'il en soit, je vous laisse regarder la documentation fournit avec votre
compilateur. Bon voici l'exemple de ce court article :
fichier : main.c
/* Fichier principal
contient la fonction main
Par Golio Junior pour Falk'mag 5
*/
#include <stdio.h> /* inclusion des prototypes de printf et scanf */
#include "biblio.h" /* inclusion des prototypes de som et mul */
float sous(float a, float b)
{ return a-b;
}
int main(void)
{ int choix, fin=0;
float a, b;
while(fin==0)
{ printf (" Micro calculette\n ________________\n 1 : Addition\n \
2 : Soustraction\n 3 : Multiplication\n 10 : Fin\n Votre choix : ");
scanf("%d",&choix);
switch (choix)
{ case 1:
printf(" 1er nombre : ");
scanf("%f",&a);
printf(" 2nd nombre : ");
scanf("%f",&b);
printf("Résultat : %f\n", som(a,b));
break;
case 2:
printf(" 1er nombre : ");
scanf("%f",&a);
printf(" 2nd nombre : ");
scanf("%f",&b);
printf("Résultat : %f\n", sous(a,b));
break;
case 3:
printf(" 1er nombre : ");
scanf("%f",&mul_op1);
printf(" 2nd nombre : ");
scanf("%f",&mul_op2);
mul();
printf("Résultat : %f\n", resul_mul);
break;
case 10:
fin=1;
break;
}
printf(" %dième utilisation\n",compteur());
}
return 0;
}
fichier : biblio.c
/* bibliothèque d'exemple */
/* déclaration des variables globales */
float mul_op1, mul_op2, resul_mul;
/* déclaration des fonctions */
static float sous(float a, float b)
{ return a*b; /* cette fonction n'est pas exportée
elle restera donc interne au module
heureusement, car son résultat ne correspond
pas son nom!
*/
}
int compteur(void)
{ static a=1;
return a++; /* la valeur de a va être incrémenté à chaque appel
c'est un exemple classique d'utilisation de
variable statique
*/
}
float som(float a, float b)
{ return a+b;
}
void mul(void)
{ resul_mul=mul_op1*mul_op2;
}
fichier : biblio.h
/* prototypage de fonctions */
extern float som(float a, float b);
extern void mul(void);
extern int compteur(void);
/* variables externes */
extern float mul_op1, mul_op2, resul_mul;
Pour saisir l'interet des variables ou des fonctions déclarées en static, changez la classe de la fonction sous dans biblio.c. Cela produit une erreur dans l'édition de
liens, car il y a deux fonctions qui porte le même nom. Passons à la deuxième partie de cet article : les entrées/sorties. Nous avons déjà
utilisée les entrées/sorties formatées : printf et scanf. Je ne reviendrait pas sur l'utilisation de ces dernières, sauf que scanf retourne soit le nombre de champ
saisie, soit EOF. Le code EOF indique la fin du fichier; cela peut parraître absurde pour une lecture clavier, mais ça l'est moins lorsqu'il s'agit de fichier.
Nous allons voir d'autres fonctions d'entrées/sorties : getchar et putchar. Elles permettent de lire et d'écrire un caractère. Une remarque : les
entrées/sorties en C sont bufférisées (cela vient d'UNIX (je crois)), et donc lors de la lecture des caractères (cf ent_so#1.c), ils sont envoyés à
getchar, mais lors de l'appuui sur la touche entrée, le buffer est envoyé à l'entrée standard (stdin), et donc getchar récupère encore tout le tampon!
Pour essayer, tapez du texte (qui est écrit en echo), et appuyez sur entrée. voici le source du premier exemple :
fichier ent_so#1.c :
/* exemple d'entrée bufférisée
Pour falk'mag 5 par Golio Junior
*/
#include
int main (void)
{ int s=0, n, c;
printf(" Calcul de somme d'entier,\
entrer des entiers, terminer par ^Z\n");
while (scanf("%d", &n)!=EOF)
{ s+=n;
}
printf("somme : %d\n",s);
printf("lecture au clavier des caractères,\
et affichage des caractères\n");
printf("utilisation de getchar et putchar\n");
while((c=getchar())!=EOF)
{ putchar(c);
}
printf("lecture au clavier des caractères,\
et affichage des caractères\n");
printf("utilisation de getc et putc\n");
while((c=getc(stdin))!=EOF)
{ putc(c,stdout);
}
return 0;
}
Il faut savoir que sous C il existe des entrées/sortie standard qui portent le nom de stdout (sortie standard), stdin (entrée standard), stderr (sortie standard d'erreur).
Ainsi les instructions getc et putc permettent la lecture et l'écriture d'un caractère sur les entrées/sorties spécifiées ou sur un fichier. Donc getc(stdin)
est équivalent à getchar (Rq. : en fait, getchar est une macro qui utilise getc). Chacune de ces fonctions retourne EOF lorsque la fin de fichier est atteinte.
Attaqons les fichiers. Pour écrire dans un fichier, il faut d'abord l'ouvrir. c'est ce que réalise la fonction fopen. Cette fonction retourne un pointeur sur le descripteur
du fichier ouvert, ou NULL si il y a une erreur. Les paramètres de la fonction fopen sont : le nom du fichier (en fait l'adresse d'une chaîne de caractère contenant le nom
de fichier) et le mode (sous forme d'une chaîne). Le mode du fichier permet d'ouvrir un fichier :
- en lecture ("r")
- en écriture ("w") (éfface l'ancien fichier)
- en allongement ("a") (ajout en fin de fichier)
- en lecture pour mise à jour ("r+")
- en écriture et en lecture ("w+")
- en allongement et en lecture ("a+")
A tous ces modes, on peut rajouter (à la fin de la chaîne) "b", qui signifie que les fichiers seront traités comme des fichiers binaires. Par exemple le
caractère \n ne serat pas transformé en NL+RET (par exemple). Pour lire/écrire un fichier, soit on utilise les fonctions getc ou putc, ou (mieux?) les fonction fprintf et
fscanf qui ont le même fonctionnemnt que pour les entrées/sorties standards, sauf quelles s'appliquent aux fichiers. Pour refermer un fichier, rien de plus simple, il suffit
d'appeler la fonction fclose, qui retourne vrai si il y a une erreur. Bon voici les petits programmes d'exemple :
fichier ent_so#2.c
/* entrée/sortie sur fichier
pour Falk'mag 5, par Golio junior
*/
#include
int main(void)
{ FILE *f;
int c;
/* ouverture du fichier en écriture toto.out */
if ((f=fopen("toto.out","w"))==NULL)
{ printf ("impossible d'ouvrir toto.out\n");
}
else
{ printf("tapez votre texte jusqu'a ^Z\n");
while((c=getchar())!=EOF)
{ fputc(c,f);
}
if (fclose(f))
{ printf("erreur de fermeture\n");
}
printf(" voici ce que vous avez tapez :\n");
if ((f=fopen("toto.out","r"))==NULL)
{
}
else
{ while((c=fgetc(f))!=EOF)
{ putc(c,stdout);
}
fclose(f);
}
}
return 0;
}
fichier ent_so#3.c
/* exemple d'entrées/sorties formatée
par golio Junior, pour Falk'mag 5
*/
#include
int main(void)
{ FILE *f;
int n;
if ((f=fopen("titi.out","w"))==NULL)
{ printf ("impossible d'ouvrir le fichier titi.out\n");
}
else
{ printf("entrer des entiers, terminer par ^Z\n");
while (scanf("%d", &n)!=EOF)
{ fprintf(f, "%d ", n); /* attention l'espace sert de séparateur! */
}
if (fclose(f))
{ printf("erreur de fermeture\n");
}
}
printf("relecture et affichage des entiers :\n");
if ((f=fopen("titi.out","r"))==NULL)
{ printf("impossible d'ouvrir le fichier titi.out\n");
}
else
{ while(fscanf(f,",%d",&n)!=EOF)
{ printf("%d\n",n);
}
fclose(f);
}
return 0;
}
Et voilà la fin de cette initiation au C. Si vous avez des remarques, des suggestions pour d'autres articles, pas de problème, mon adresse n'a pas changé (cf. article
crédits), et ma nouvelle adresse e-mail est la suivante (valable jusqu'au 14 Juin 1997) :
Bertrand.Jouin@ens.insa-rennes.fr"
Bon C!
Golio Junior