Next Previous Contents

10. La librairie C Standard pour Linux

Par James M. Rogers mailto:jrogers@u.washington.edu

Le dernier article était sur <ctype.h> la gestion des caractères. Cet article est sur <stdlib.h> qui contient plusieurs petites sections : math sur les entiers, trier et chercher, les nombres aléatoires, les conversions chaînes vers nombre, les conversions des caractères multi-octet, l'allocation mémoire et les fonctions d'environnement. Parce que cette librairie contient plusieurs petites sections mais de grande importance, je parlerai de chacun de ces groupes dans sa propre section. Un exemple sera donné dans chaque section plus bas parce que ces fonctions sont trop variées pour avoir un seul exemple pour toutes.

Je suppose une connaissance de la programmation C de la part du lecteur. Il n'y a aucune garantie sur l'exactitude de n'importe quelle information ni sur leur pertinence quelle qu'en soit la raison.

Comme toujours, si vous trouvez une erreur dans ma documentation dites-le moi et je corrigerais moi-même dans un document ultérieur. Consulter les corrections à la fin de ce document pour une liste des corrections des précédents articles.

10.1 Mathématiques des Entiers


    #include <stdlib.h> 

    int      abs(int x); 
    div_t    div(int numérateur, int dénominateur); 
    long int labs(long int x); 
    ldiv_t   ldiv(long int numérateur, long int dénominateur);

int x 
int numérateur
int dénominateur
Les versions  long int sont les mêmes que les trois arguments int.

abs retourne la valeur absolue de l'argument.

div retourne une structure de donnée qui contient à la fois le quotient et le reste.

labs est la version long de la fonction abs.

ldiv est la version long de la fonction div.

Les Mathématiques sur les Entiers sont les mathématiques sur les nombres sans virgules. Pas de fractions. Ce sont des maths de primaire [NdR: le malheureux ! Il croit que l'arithmétique se limite à ce qu'on apprend en primaire.]. Si vous vous rappelez que le numérateur est divisé par le dénominateur et que la réponse est le quotient avec les trucs laissés comme étant le reste alors vous y êtes. Div_t et ldiv_t sont des structures qui contiennent le quotient et le reste. Ces structures ressemblent à :


struct div_t { 
    int quot; 
    int rem; 
} 

struct ldiv_t { 
    long int quot; 
    long int rem; 
} 

Ces types sont déjà définis pour vous dans la librairie <stdlib.h>. Le Fichier d'exemple montre quelques façons d'utiliser ces quatres fonctions.

10.2 Les Conversions Chaîne vers Nombre


#include <stdlib.h> 

    double   atof(const char *chaîne); 
    int      atoi(const char *chaîne); 
    long int atol(const char *chaîne); 
    double   strtod(const char *chaîne, char **endptr); 
    long int strtol(const char *chaîne, char **endptr, int base); 
    unsigned long int strtoul(const char *chaîne, char **endptr, int base);

const char *chaîne
char **endptr 
int base 

atof est une conversion ascii vers un flottant.

atoi est une conversion ascii vers un entier.

atol est une conversion ascii vers un long.

strtod est une conversion chaîne vers double.

strtol est une conversion chaîne vers long et la chaîne peut contenir des nombres dans une base autre que la base 10.

strtoul est pareil que strtol, sauf qu'elle retourne un long non signé.

Si vous lisez un nombre sur l'entrée utilisateur, vous aurez besoin d'utiliser ces routines pour convertir les caractères '1' '2' '3' en nombre 123. La façon la plus facile pour convertir de l'autre manière, d'un nombre vers une chaîne, est d'utiliser la fonction sprintf().

Le programme d'exemple est juste un échantillon d'utilisations de chacune des commandes ci-dessus.

10.3 Chercher et Trier


#include <stdlib.h> 

    void qsort(void *base, size_t num_of_objs, size_t size_of_obj, int (*compar)(const void *, const void *)); 
    void bsearch(const void *key, void *base, size_t num_of_objs, size_t size_of_obj, int
    (*compar)(const void *, const void *));

void *base 
size_t num_of_objs 
size_t size_of_obj 
const void * 
const void *key 

qsort triera le tableau de chaîne en utilisant une fonction de comparaison que vous écrirez vous même.

bsearch cherchera à travers le tableau trié en utilisant une fonction de comparaison que vous écrirez vous même.

Vous n'avez pas besoin d'écrire votre propre routine de tri. A travers l'utilisation de ces fonctions vous pouvez trier et chercher dans les tableaux en mémoire.

Il est important de réaliser que vous devez trier un tableau avant de pouvoir faire une recherche à cause de la méthode de recherche utilisée.

Dans le but de générer des données pour avoir quelque chose à trier j'ai combiné cet exemple avec le générateur de nombre aléatoire. J'ai initialisé un tableau de chaîne avec une série de nombre aléatoire puis je l'ai trié. J'ai alors cherché si la chaîne 1000 était dans la table. J'ai finalement affiché le tableau trié.

10.4 Allocation Mémoire


#include <stdlib.h> 

    void *calloc(size_t num_of_objs, size_t size_of_objs); 
    void free(void *pointer_to_obj); 
    void *malloc(size_t size_of_object); 
    void *realloc(void *pointer_to_obj, size_t size_of_obj);

size_t num_of_objs 
size_t size_of_objs 
void *pointer_to_obj 

free libèrera la zone mémoire indiquée qui a été allouée précédemment. Vous générerez un fichier core si vous tentez de libérer la mémoire deux fois.

malloc allouera le nombre d'octets indiqués et retournera un pointeur sur la zone mémoire.

calloc allouera le tableau et retournera un pointeur dessus.

realloc permet de changer la taille de la zone mémoire "à la volée". Vous pouvez diminuer ou augmenter la mémoire selon vos besoins, soyez conscient qu'essayer d'accéder à la mémoire au-delà de ce que vous avez alloué provoquera un fichier core.

Les allocations mémoires dynamiques (lors de l'exécution du programme) vous permettent d'écrire un programme qui utilise seulement la mémoire dont il a besoin pour fonctionner. Pas besoin de changer une valeur et de recompiler si vous demandez de la mémoire lors de l'exécution. Aussi pas besoin de définir les tableaux à la taille maximum possible lorsque la tailler moyenne réelle est une fraction de la taille maximum.

Le danger d'utiliser la mémoire de cette façon est que dans un programme complexe il est facile d'oublier de libérer la mémoire une fois utilisée. Ces "fuites mémoires" pourront éventuellement avoir pour conséquence que votre programme utilisera toute la mémoire disponible sur un système et provoqueront un vidage mémoire (core dump). Il est aussi important de ne pas supposer qu'une allocation mémoire marchera toujours. Essayer d'utiliser un pointeur sur un emplacement mémoire n'appartenant pas à votre programme provoquera un core dump. Un problème plus sérieux est quand un pointeur réécrit sur la propre mémoire du programme. Cela aura pour conséquence un programme fonctionnant de manière erratique et une plus grande difficulté à cerner le problème exact.

J'ai eu à écrire deux exemples différents pour démontrer toute la diversité de ces fonctions. Dans le but de démontrer réellement leur utilisation j'ai eu à réellement programmer quelque chose à moitié utile.

Le premier exemple est un programme de pile qui alloue et désalloue la mémoire lorsque vous empilez (push) ou désempilez (pop) les valeurs de la pile.

Le deuxième exemple lit n'importe quel fichier et le met dans la mémoire de l'ordinateur, réallouant la mémoire à la volée. J'ai laissé les tests de debug dans le second programme pour que vous puissiez voir que la mémoire est seulement réallouée lorsque le programme a besoin de plus de mémoire.

10.5 L'Environnement


#include <stdlib.h> 

    void abort ( void ); 
    int atexit ( void ( *func )( void ) ); 
    void exit ( int status); 
    char *getenv( const char *string); 
    int setenv ( const char *name, const char *value, int overwrite ); 
    int unsetenv ( const char *name, const char *value, int overwrite ); 
    int system ( const char *string );

void 
void (*func)(void) 
int status 
const char *string 
const char *name 
const char *value 
int overwrite 

abort provoque l'envoi du signal SIGABORT à votre programme. A moins que votre programme gère ce signal, il se terminera avec une erreur d'abandon (abort error).

atexit vous permettera d'exécuter un ensemble d'appels de fonctions lors de la fin (exit) du programme. Vous pouvez en empiler pas mal, je pense me souvenir que vous pouvez en avoir jusqu'à 32.

exit terminera votre programme avec l'entier indiqué comme valeur de retour.

getenv retournera la valeur de la variable d'environnement spécifiée ou la valeur NULL si la variable d'environnement n'est pas positionnée.

setenv créera la variable indiquée avec la valeur spécifiée, renverra -1 sur une erreur.

unsetenv supprimera la variable indiquée.

system exécutera la commande chaîne indiquée et retournera la valeur de sortie (exit) de la commande.

Ces fonctions vous permettent de vous connecter en retour à l'environnement unix à partir duquel vous avez lancé votre programme et de fixer des valeurs de sortie (exit), lire les valeurs des variables d'environnement et lancer des commandes dans un programme c.

Le programme d'exemple montre comment lire une variable d'environnement et les deux méthodes différentes de créer celles-ci. Exécuter ce programme sans avoir créé la variable TESTING puis faire `export TESTING=ce-que-vous-voulez` et lancer le programme à nouveau. Vous remarquerez la différence entre les deux exécutions. Noter aussi l'ordre d'appel de fonction atexit() et l'ordre où elle réellement appelée lorsque le programme sort. Copier un des appels abort() avant l'appel à exit et réexécuter le programme, lorsque abort est appelé les fonctions atexit() ne sont pas appelées.

10.6 Les Nombres Aléatoires


#include <stdlib.h> 

    int rand(void); 
    void srand(unsigned int seed);

void 
unsigned int seed 

rand retournera une valeur aléatoire entre 0 et RAND_MAX.

seed commence une nouvelle séquence de nombre pseudo-aléatoires.

La fonction rand positionnera la graine (seed) à 1 la première fois que vous appellerez rand dans votre programme sauf si vous l'avez positionnée quelque part ailleurs. La séquence de nombres aléatoires que vous avez obtenu de rand sera dans le même ordre si vous affectez à la graine la même valeur à chaque fois. Pour s'approcher des vrais nombres aléatoires vous devez affecter à la graine quelque chose qui ne se répète pas. time() est ce que j'utilise dans cet exemple.

L'exemple de cette section a été combiné avec le tri et la recherche.

10.7 La Conversion MultiOctets


    #include <stdlib.h> 
    int mblen(const char *s, size_t n); 
    int mbtowc(wchar_t *pwc, const char *s, size_t n); 
    int wctomb(char *s, wchar_t wchar); 
    size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n); 
    size_t mbstowcs(char *s, wchar_t *pwcs, size_t n);

C'est le nouveau bidule de mappage de caractères multilangue. Je ne pense pas que je suis pour l'instant qualifié pour en parler. J'y reviendrais une fois que j'aurais parlé de tout le reste. Ou peut être quelqu'un d'autre pourrait nous dire comment les utiliser dans la programmation de tous les jours.

10.8 Bibliographie

The ANSI C Programming Language, Second Edition, Brian W. Kernighan, Dennis M. Ritchie, Printice Hall Software Series, 1988

The Standard C Library,P. J. Plauger, Printice Hall P T R, 1992

The Standard C Library, Parts 1, 2, and 3, Chuck Allison, C/C++ Users Journal, January, February, March 1995

STDLIB(3), BSD MANPAGE Linux Programmer's Manual, 29 November 1993

Articles précédents sur "La librairie C Standard sous Linux

Copyright 1999, James M. Rogers - Publié dans le numéro 39 de la Linux Gazette, avril 1999

Adaptation française : Frédéric Gacquer (Neuronnexion)


Next Previous Contents