15 Bibliothèques¶

Fig. 15.1 Bibliothèque du Trinity College de Dublin¶
Une bibliothèque informatique est une collection de fichiers comportant des fonctionnalités logicielles prêtes à l'emploi. La fonction printf
est une de ces fonctionnalités et offerte par le header <stdio.h>
faisant partie de la bibliothèque libc6
.
L'anglicisme library, plus court à prononcer et à écrire est souvent utilisé en lieu et place de bibliothèque tant il est omniprésent dans le monde logiciel. Le terme <stdlib.h>
étant la concaténation de standard library par exemple. Notez que librairie n'est pas la traduction correcte de library qui est un faux ami.
Une library, à l'instar d'une bibliothèque, contient du contenu (livre écrit dans une langue donnée) et un index (registre). En informatique il s'agit d'un fichier binaire compilé pour une architecture donnée ainsi qu'un ou plusieurs fichiers d'en-tête (header) contenant les définitions de cette bibliothèque.
Dans ce chapitre on donnera plusieurs exemples sur un environnement POSIX. Sous Windows, les procédures choses sont plus compliquées, mais les concepts restent les mêmes.
15.1 Exemple: libgmp¶
Voyons ensemble le cas de libgmp. Il s'agit d'une bibliothèque de fonctionnalités très utilisée et permettant le calcul arithmétique multiprécision en C. En observant le détail du paquet logiciel Debian on peut lire que libgmp
est disponible pour différentes architectures amd64
, arm64
, s390x
, i386
, ... Un développement sur un Raspberry-PI nécessitera arm64
alors qu'un développement sur un PC utilisera amd64
. En cliquant sur l'architecture désirée on peut voir que ce paquet se compose des fichiers suivants (list réduite aux fichiers concernant C :
# Fichier d'en-tête C
/usr/include/x86_64-linux-gnu/gmp.h
# Bibliothèque compilée pour l'architecture visée (ici amd64)
/usr/lib/x86_64-linux-gnu/libgmp.a
/usr/lib/x86_64-linux-gnu/libgmp.so
# Documentation de la libgmp
/usr/share/doc/libgmp-dev/AUTHORS
/usr/share/doc/libgmp-dev/README
/usr/share/doc/libgmp-dev/changelog.gz
/usr/share/doc/libgmp-dev/copyright
On a donc :
gmp.h
Fichier d'en-tête à include dans un fichier source pour utiliser les fonctionnalités
libgmp.a
Bibliothèque statique qui contient l'implémentation en langage machine des fonctionnalités à référer au linker lors de la compilation
libgmp.so
Bibliothèque dynamique qui contient aussi l'implémentation en langage machine des fonctionnalités
Imaginons que l'on souhaite bénéficier des fonctionnalités de cette bibliothèque pour le calcul d'orbites pour un satellite d'observation de Jupyter. Pour prendre en main cet libary on écrit ceci :
#include <gmp.h>
#include <stdio.h>
int main(void)
{
unsigned int radix = 10;
char a[] = "19810983098510928501928599999999999990";
mpz_t n;
mpz_init(n);
mpz_set_ui(n, 0);
mpz_set_str(n, a, radix);
mpz_out_str(stdout, radix, n);
putchar('\n');
mpz_add_ui(n, n, 12); // Addition
mpz_out_str(stdout, radix, n);
putchar('\n');
mpz_mul(n, n, n); // Square
mpz_out_str(stdout, radix, n);
putchar('\n');
mpz_clear(n);
}
Puis on compile :
$ gcc gmp.c
gmp.c:1:10: fatal error: gmp.h: No such file or directory
#include <gmp.h>
^~~~~~~
compilation terminated.
Aïe! La bibliothèque n'est pas installée.
- Debian/Ubuntu
$ sudo apt-get install libgmp-dev
- Mac OS X
$ brew install gmp
- Windows
ERREUR 404
Deuxième tentative :
$ gcc gmp.c
/tmp/cc2FxDSy.o: In function `main':
gmp.c:(.text+0x6f): undefined reference to `__gmpz_init'
gmp.c:(.text+0x80): undefined reference to `__gmpz_set_ui'
gmp.c:(.text+0x96): undefined reference to `__gmpz_set_str'
gmp.c:(.text+0xb3): undefined reference to `__gmpz_out_str'
gmp.c:(.text+0xd5): undefined reference to `__gmpz_add_ui'
gmp.c:(.text+0xf2): undefined reference to `__gmpz_out_str'
gmp.c:(.text+0x113): undefined reference to `__gmpz_mul'
gmp.c:(.text+0x130): undefined reference to `__gmpz_out_str'
gmp.c:(.text+0x146): undefined reference to `__gmpz_clear'
collect2: error: ld returned 1 exit status
Cette fois-ci on peut lire que le compilateur à fait sont travail, mais ne parvient pas à trouver les symboles des fonctions que l'on utilise p.ex. __gmpz_add_ui
. C'est normal parce que l'on n'a pas renseigné la bibliothèque à utiliser.
$ gcc gmp.c -lgmp
$ ./a.out
19810983098510928501928599999999999990
19810983098510928501928600000000000002
392475051329485669436248957939688603493163430354043714007714400000000000004
Cette manière de faire utilise le fichier libgmp.so
qui est la bibliothèque dynamique, c'est-à-dire que ce fichier est nécessaire pour que le programme puisse fonctionner. Si je donne mon exécutable à un ami qui n'as pas install libgmp sur son ordinateur, il ne sera pas capable de l'exécuter.
Alternativement on peut compiler le même programme en utilisant la librairie statique
$ gcc gmp.c /usr/lib/x86_64-linux-gnu/libgmp.a
C'est-à-dire qu'à la compilation toutes les fonctionnalités ont été intégrées à l'exécutable et il ne dépend de plus rien d'autre que le système d'exploitation. Je peux prendre ce fichier le donner à quelqu'un qui utilise la même architecture et il pourra l'exécuter. En revanche, la taille du programme est plus grosse :
# ~167 KiB
$ gcc gmp.c -l:libgmp.a
$ size a.out
text data bss dec hex filename
155494 808 56 156358 262c6 ./a.out
# ~8.5 KiB
$ gcc gmp.c -lgmp
$ size a.out
text data bss dec hex filename
2752 680 16 3448 d78 ./a.out
15.2 Exemple: ncurses¶
La bibliothèque ncurses traduction de nouvelles malédictions est une évolution de curses développé originellement par Ken Arnold . Il s'agit d'une bibliothèque pour la création d'interfaces graphique en ligne de commande, toujours très utilisée.
La bibliothèque permet le positionnement arbitraire dans la fenêtre de commande, le dessin de fenêtres, de menus, d'ombrage sous les fenêtres, de couleurs ...

Fig. 15.2 Exemple d'interface graphique écrite avec ncurses. Ici la configuration du noyau Linux.¶
L'écriture d'un programme Hello World avec cette bibliothèque pourrait être :
#include <ncurses.h>
int main()
{
initscr(); // Start curses mode
printw("hello, world"); // Print Hello World
refresh(); // Print it on to the real screen
getch(); // Wait for user input
endwin(); // End curses mode
return 0;
}
La compilation n'est possible que si :
La bibliothèque est installée sur l'ordinateur
Le lien vers la bibliothèque dynamique est mentionné à la compilation
$ gcc ncurses-hello.c -ohello -lncurses
15.3 Bibliothèques statiques¶
Une static library est un fichier binaire compilé pour une architecture donnée et portant les extensions :
.a
sur un système POSIX (Android, Mac OS, Linux, Unix).lib
sous Windows
Une bibliothèque statique n'est rien d'autre qu'une archive d’un ou plusieurs objets. Rappelons-le un objet est le résultat d'une compilation.
Par exemple si l'on souhaite écrire une bibliothèque statique pour le code de César on écrira un fichier source caesar.c:
#include <stdio.h>
void caesar(char str[], unsigned key)
{
key %= 26;
for (size_t i = 0; str[i]; i++)
{
char c = str[i];
if (c >= 'a' && c <= 'z')
{
str[i] = ((c + key > 'z') ? c - 'z' + 'a' - 1 : c) + key;
}
else if (c >= 'A' && c <= 'Z')
{
str[i] = ((c + key > 'Z') ? c - 'Z' + 'A' - 1 : c) + key;
}
}
}
Ainsi qu'un fichier d'en-tête caesar.h:
/**
* Function that compute the Caesar cipher
* @param str input string
* @param key offset to add to each character
*/
void caesar(char str[], unsigned key);
Pour créer une bibliothèque statique rien de plus facile. Le compilateur crée l'objet, l'archiver crée l'amalgame :
$ gcc -c -o caesar.o caesar.c
$ ar rcs caesar.a caesar.o
Puis il suffit d'écrire un programme pour utiliser cette bibliothèque :
#include <caesar.h>
#include <stdio.h>
#define KEY 13
int main(int argc, char *argv[])
{
for (int i = 1; i < argc; i++)
{
caesar(argv[i], KEY);
printf("%s\n", argv[i]);
}
}
Et de compiler le tout. Ici on utilise -I.
et -L.
pour dire au compilateur de chercher le fichier d'en-tête et la bibliothèque dans le répertoire courant.
$ gcc encrypt.c -I. -L. -l:caesar.a
La procédure sous Windows est plus compliquée et ne sera pas décrite ici.
15.4 Bibliothèques dynamiques¶
Une dynamic library est un fichier binaire compilé pour une architecture donnée et portant les extensions :
.so
sur un système POSIX (Android, Mac OS, Linux, Unix).dll
sous Windows
L'avantage principal est de ne pas charger pour rien chaque exécutable compilé de fonctionnalités qui pourraient très bien être partagées. L'inconvénient est que l'utilisateur du programme doit impérativement avoir installé la bibliothèque. Dans un environnement POSIX les bibliothèques dynamiques disposent d'un emplacement spécifique ou elles sont toute stockées. Malheureusement sous Windows le consensus est plus partagé et il n'est pas rare de voir plusieurs applications différentes héberger une copie des dll localement si bien que l'avantage de la bibliothèque dynamique est anéanti par un défaut de cohérence.
Reprenant l'exemple de César vu plus haut, on peut créer une bibliothèque dynamique :
$ gcc -shared -o libcaesar.so caesar.o
Puis compiler notre programme pour utiliser cette bibliothèque. Avec une bibliothèque dynamique, il faut spécifier au compilateur quels sont les chemins vers lesquels il pourra trouver les bibliothèques installées. Comme ici on ne souhaite pas installer la bibliothèque et la rendre disponible pour tous les programmes, il faut ajouter aux chemins par défaut, le chemin local $(pwd .)
, en créant une variable d'environnement nommée LIBRARY_PATH
.
$ LIBRARY_PATH=$(pwd .) gcc encrypt.c -I. -lcaesar
Le problème est identique à l'exécution, car il faut spécifier (ici avec LD_LIBRARY_PATH
) le chemin ou le système d'exploitation s'attendra à trouver la bibliothèque.
$ LD_LIBRARY_PATH=$(pwd .) ./a.out ferrugineux
sreehtvarhk
Car sinon c'est l'erreur :
$ LIBRARY_PATH=$(pwd .) ./a.out Hey?
./a.out: error while loading shared libraries: libcaesar.so :
cannot open shared object file: No such file or directory
15.5 Bibliothèques standard¶
Les bibliothèques standard (C standard library) sont une collection normalisée d'en-têtes portables. C'est à dire que quelque soit le compilateur et l'architecture cible, cette collection sera accessible.
Le standard C99 définit un certain nombre d'en-têtes dont les plus utilisés (et ceux utilisés dans ce cours) sont :
<assert.h>
Contient la macro
assert
pour valider certains prérequis.<complex.h>
Pour manipuler les nombres complexes
<float.h>
Contient les constantes qui définissent la précision des types flottants sur l'architecture cible.
float
etdouble
n'ont pas besoin de cet en-tête pour être utilisés.<limits.h>
Contient les constantes qui définissent les limites des types entiers.
<math.h>
Fonctions mathématiques
sin
,cos
, ...<stdbool.h>
Défini le type booléen et les constantes
true
etfalse
.<stddef.h>
Défini certaines macros comme
NULL
<stdint.h>
Défini les types standard d'entiers (
int32_t
,int_fast64_t
, ...).<stdio.h>
Permet l'accès aux entrées sorties standard (
stdin
,stdout
,stderr
). Définis entre autres la fonctionprintf
.<stdlib.h>
Permet l'allocation dynamique et défini
malloc
<string.h>
Manipulation des chaînes de caractères
<time.h>
Accès au fonctions lecture et de conversion de date et d'heure.
Exercice 15.1¶
La fonction Arc-Cosinus acos
est-elle définie par le standard et dans quel fichier d'en-tête est-elle déclarée? Un fichier d'en-tête se termine avec l'extension .h
.
Exercice 15.2¶
Lors du formatage d'une date, on y peut y lire %w
, par quoi sera remplacé ce token ?
15.5.1 Fonctions d'intérêt¶
Il serait inutile ici de lister toutes les fonctions, les bibliothèques standard étant largement documentées sur internet. Il ne fait aucun doute que le développeur sera trouver comment calculer un sinus avec la fonction sin
. Néanmoins l'existence de certaines fonctions peut passer inaperçues et c'est de celles-ci don't j'aimerais parler.
15.5.1.1 Math¶
Constantes |
Description |
---|---|
|
Valeur de \(\pi\) |
|
Valeur de \(e\) |
|
Valeur de \(1/\sqrt(2)\) |
Fonction |
Description |
---|---|
|
Exponentielle \(e^x\) |
|
Exposant d'un nombre flottant \(x\cdot2^n\) |
|
Logarithme binaire \(\log_{2}(x)\) |
|
Logarithme décimal \(\log_{10}(x)\) |
|
Puissance \(x^y\) |
|
Racine carrée \(\sqrt(x)\) |
|
Racine cubique \(\sqrt[3](x)\) |
|
Hypoténuse optimisé \(\sqrt(x^2 + y^2)\) |
|
Arrondi à l'entier supérieur |
|
Arrondi à l'entier inférieur |
Notons par exemple que la fonction hypot
peut très bien être émulée facilement en utilisant la fonction sqrt
. Néanmoins elle existe pour deux raisons élémentaires :
Éviter les dépassements (overflow).
Une meilleure optimisation du code.
Souvent, les processeurs sont équipés de coprocesseurs arithmétiques capables de calculer certaines fonctions plus rapidement.
15.5.1.2 Chaînes de caractères¶
strcopy(dst, src)
Identique à
memcpy
mais sans nécessité de donner la taille de la chaîne puisqu'elle se termine par\0
memmove(dst, src, n)
Identique à
memcpy
mais traite les cas particuliers lorsque les deux régions mémoire se superposent.
15.5.1.3 Types de données¶
Test d'une propriété d'un caractère passé en paramètre
Fonction |
Description |
---|---|
|
une lettre ou un chiffre |
|
une lettre |
|
un caractère de commande |
|
un chiffre décimal |
|
un caractère imprimable ou le blanc |
|
une lettre minuscule |
|
un caractère imprimable (pas le blanc) |
|
un caractère imprimable pas isalnum |
|
un caractère d'espace blanc |
|
une lettre majuscule |
|
un chiffre hexadécimal |
15.5.1.4 Limites¶
Constante |
Valeur |
---|---|
|
-128 |
|
+127 |
|
0 |
|
255 |
|
-32768 |
|
+32767 |
|
65535 |
|
-2147483648 |
|
+2147483647 |
|
+4294967295 |
|
1E+37 ou plus |
|
1E-9 ou moins |
15.6 Autres bibliothèques¶
GNU C Library (glibc) - C11 - POSIX.1-2008 - IEEE 754-2008
15.6.1 POSIX C Library¶
Le standard C ne définit que le minimum vital et qui est valable sur toutes les architectures pour autant que la toolchain soit compatible C99. Il existe néanmoins toute une collection d'autres fonctions manquantes :
La communication entre les processus (deux programmes qui souhaitent communiquer entre eux) -
<sys/socket.h>
-<sharedmemory.h>
La communication sur le réseau e.g. internet -
<sys/socket.h>
-<arpa/inet.h>
-<net/if.h>
Les tâches -
<thread.h>
Les traductions de chaînes p.ex. français vers anglais -
<iconv.h>
Les fonctions avancées de recherche de texte -
<regex.h>
Le log centralisé des messages (d'erreur) -
<syslog.h>
Toutes ces bibliothèques additionnelles ne sont pas nécessairement disponibles sur votre ordinateur ou pour le système cible, surtout si vous convoitez une application bare-metal. Elles dépendent grandement du système d'exploitation utilisé, mais une tentative de normalisation existe et se nomme POSIX (ISO/IEC 9945).
Généralement la vaste majorité des distributions Linux et Unix sont compatibles avec le standard POSIX et les bibliothèques ci-dessus seront disponibles à moins que vous ne visiez une architecture différente de celle sur laquelle s'exécute votre compilateur.
Le support POSIX sous Windows (Win32) n'est malheureusement que partiel et il n'est pas standardisé.
Un point d'entrée de l'API POSIX est la bibliothèque <unistd.h>
.
15.6.2 GNU GLIBC¶
La bibliothèque portable GNULIB est la bibliothèque standard référencée sous Linux par libc6
.
15.6.3 Windows C library¶
La bibliothèque Windows Windoes API offre une interface au système de fichier, au registre Windows, aux imprimantes, à l'interface de fenêtrage, à la console et au réseau.
L'accès à cet API est offert par un unique point d'entrée windows.h qui regroupe certains en-têtes standards (<stdarg.h>
, <string.h>
, ...), mais pas tous (😔) ainsi que les en-têtes spécifiques à Windows tels que :
<winreg.h>
Pour l'accès au registre Windows
<wincon.h>
L'accès à la console
La documentation est disponible en ligne depuis le site de Microsoft, mais n'est malheureusement pas complète et souvent il est difficile de savoir sur quel site trouver la bonne version de la bonne documentation. Par exemple, il n'y a aucune documentation claire de LSTATUS pour la fonction RegCreateKeyExW permettant de créer une entrée dans la base de registre.
Un bon point d'entrée est le Microsoft API and reference catalog.
Quelques observations :
Officiellement Windows est compatible avec C89 (ANSI C) (c.f. C Language Reference)
L'API Windows n'est pas officiellement compatible avec C99, mais elle s'en approche, il n'y pas ou peu de documents expliquant les différences.
Microsoft n'a aucune priorité pour développer son support C, il se focalise davantage sur C++ et C#, c'est pourquoi certains éléments du langage ne sont pas ou peu documentés.
Les types standards Windows différent de ceux proposés par C99. Par exemple
LONG32
remplaceint32_t
.