* * *
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 :



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 :


 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