280 likes | 386 Views
Communication avec l’environnement. Contenu. Arguments de la ligne de commande Terminaison d’un programme Communication avec l’environnement Les signaux Gestion d’erreur. Les arguments reçus par la fonction main.
E N D
Contenu • Arguments de la ligne de commande • Terminaison d’un programme • Communication avec l’environnement • Les signaux • Gestion d’erreur
Les arguments reçus par la fonction main • La norme prévoit qu’un programme exécuté sous le contrôle d’un environnement débute par la fonction main • Celle-ci peut disposer d’arguments lui permettant de recueillir des informations en provenance de l’environnement
L’en-tête de la fonction main • La fonction main ne dispose pas de prototype et n’est pas déclarée. • Mais son en-tête ne peut s’écrire que sous ces deux formes: • int main(void) { int i;….,return(i);} • int main(int argc, char *argv[ ]) {int i;….,return(i);} • Les en-têtes «main()» et «int main()» sont hors norme mais ne provoquent qu’un warning
Récupération des arguments • argc (argument count) donne le nombre d’arguments de la ligne de commande qui a appelé le programme (nom du prog. compris!) • argv (argument vector) est un pointeur sur un tableau de chaînes de caractères qui contient les arguments, à raison de 1 par chaîne.
Exemple Marieulle[123]>monProg –h –l test.txt argc = 4 et argv monProg\0 -h\0 -l\0 test.txt\0
Exemple simple int main(int argc, char *argv[]){ int i; printf(``nom de prog: %s\n``, argv[0]); if(argc1) for(i=1;iargc;i++) printf(``arg num %d: %s\n``,i,argv[i]); else puts(``pas d’arguments``); }
int main(int argc, char *argv[ ]){ int optionA=0, optionB=0; char nomFichier[100]; while(--argc 0 && (*++argv)[0]==`-`){ while(c= *++argv[0]){ switch(c){ case `a`: optionA=1; break; case `b`: optionB=1; break; default: printf(``option interdite %c\n``,c); argc = 0; break; } } } if(argc != 1) puts(``Usage: monProg -x –v fichier``); else strcpy(nomFichier,*argv); }
Terminaison d’un programme • Un programme peut être interrompu d’office par le SE en cas de rencontre d’une situation dite ``d’exception``: • Tentative d’exécution d’une instruction inexistante • Adresse invalide • Division par zéro… • Rencontre d’un abort dans le programme • Sortie normale dans les situations: • Appel de exit de n’importe quel point du programme • Rencontre d’un return dans la fonction main • Fin naturel de la fonction main
La fonction exit L’appel à la fonction exit provoque • la fermeture de tout les fichiers encore ouverts • La destruction des fichiers créés par tmpfile • La fin du programme • Transmission à l’environnement de son unique argument entier. La norme prévoit deux valeurs : • EXIT_SUCCESS (généralement 0) • EXIT_FAILURE (généralement non nul)
La fonction atexit int atexit(void (*fct)(void)) • Permet d’enregistrer les noms de fonctions de son choix qui seront appelés avant l’arrêt (normal) de l’exécution par exit • Ces fonctions seront appelés • dans l’ordre inverse de leur enregistrement • Avant la fermeture des fichiers encore ouverts • Avant la destruction des fichiers temporaires
Exemple #include stdlib.c … void myExit(){ puts(``I am not dead!``); } … Int main(void){ … atexit(myExit); … exit(EXIT_SUCCESS); }
L’instruction return dans la fonction main • L’instruction return peut comporter une expression mais ce n’est pas une obligation, même quand l’en-tête de la fonction précise une valeur de retour • Dans main l’expression est de type numérique (int) • Equivalent à un appel à exit
Communication avec l’environnement La norme propose, sous forme de fonctions standard, deux outils permettant: • De recueillir certaines info relatives à l’état de l’environnement (getenv) • D’envoyer une commande à l’environnement (system)
La fonction getenv • L’environnement est défini par un certain nombre de paramètres de type chaîne de caractères. • Chaque paramètre est lui-même repéré par un nom prédéfini (une chaîne de caractère) dépendant de l’implémentation. • La fonction getenv permet de connaître l’adresse d’une chaîne correspondant à la valeur d’un paramètre de nom donné. char *getenv(const char *nomParam); Le programme ne devra pas modifier la chaîne de retour
Exemple #include stdlib.c #include stdio.h int main(void){ char *terminal = getenv(``TERM``); puts(``le terminal est: ``); if(terminal == NULL) puts(``inconnu``); else printf(``un %s\n``,terminal); exit(EXIT_SUCCESS); }
Les signaux • Norme assez flou permet de mettre en place un mécanisme de gestion des exceptions et des interruptions: transmission de signaux. • Un signal est repéré par un numéro entier. • Il est émis (déclenché/levé) par une fonction (appel à raise) ou un mécanisme (rapport d’erreur du SE). • Provoque un certain traitement. (pour chaque signal prévu par l’implémentation, il y a un traitement par défaut qui peut être détourné par la fonction signal)
Remarques • La norme prévoit un certain nombre de valeurs prédéfinis de signaux (tentative de division par zéro…) • Mais n’impose pas à l’environnement de déclencher le signal correspondant lorsque l’exception est détectée !
La fonction signal void ( *signal( int numsig, void (*f)(int) ) )(int) • numsig est le numéro du signal concerné • f : traitement à associer au signal • Valeur prédéfinie (SIG_DFL ou SIG_IGN) • Fonction recevant un int sans retour • retour: • SIG_ERR en cas d’erreur • Valeur de f relative au dernier appel de signal pour ce même numéro
#include signal.h void fsig(int); int main(void){ double x=1, y=0, z; signal(SIGPFE,fsig); z=x/y; puts(``y a rien là?``); } void fsig(int n){ printf(``division par zéro, n=%d``,n); exit(EXIT_FAILURE); }
Les numéros de signaux prédéfinis • SIGABRT fin anormale (éventuellement lancé par abort) (fct de trait. du signal ne doit pas contenir de fonction standard ) • SIGFPE opération arithmétique incorrecte • SIGILL instruction invalide • SIGINT réception d’un signal interactif • SIGSEV accès mémoire invalide • SIGTERM demande d’arrêt envoyé au programme
La fonction raise int raise(int numsig); Provoque l’emission su signal numsig.
Remarque La norme prévoit que dès qu’on a traité un signal par une fonction de son choix, les déclenchements ultérieurs de ce même signal ne seront plus ``vus`` par le programme. Pour parvenir à traiter de nouveau le signal, il suffit de prévoir un appel approprié de signal à l’intérieur de la fonction de traitement du signal elle-même. void fsig(int n){ puts(``appel à fsig``); signal(n, fsig); }
Gestion d’erreur • La plupart des fonctions du système peuvent échouer pour diverse raisons. • On peut alors examiner la pseudo-variable errno pour déterminer plus précisément la cause de l’échec et agir en conséquence. #include stdio.h extern int errno; … • Sa valeur est initialisée à zéro • Elle peut être modifiée par n’importe quelle fonction, même en dehors d’une situation d’erreur
perror et strerror • La fonction perror imprime sur la sortie d’erreur standard un message décrivant la dernière erreur qui s’est produite, précédé d’une chaîne de caractère. #include stdio.h void perror(const char *s); • la fonction strerror retourne le texte du message d’erreur correspondant à un numéro #include string.h char *strerror(int errnum);
Traitement des erreurs, branchements non locaux #include setjmp.h int setjmp(jmp_buf env); void longjmp(jmp_buf env, int val); • Ces deux fonctions permettent de réaliser un branchement d’une fonction à une autre (la première doit avoir été appelée par la seconde). • Moyen de gestion d’erreur par exceptions
setjmp et longjmp • setjmp permet de sauver l’environnement (contexte d’exécution) dans le variable tampon env. et retourne 0 sauf erreur • longjmp rétablit le dernier environnement qui a été sauvé dans env par setjmp : le programme continue à l’endroit du setjmpcomme si celui-ci avait retourné la valeur val.
… #include setjmp.h jmp_buf env; extern long fact(long x); long comb(long k, long n){ if(k 0 || n 1|| k n ) longjmp(env,2); return(fact(n)/(fact(k)*fact(n-k))); } int main(void){ if(setjmp(env)){ fprintf(stderror, ``erreur de calcul!\n``); return(EXIT_FAILURE); } printf(``%ld\n``,comb(3,2)); }