Introduction
En programmation informatique, le terme magic number (en français « nombre magique ») peut référer à :
En programmation informatique, le terme magic number (en français « nombre magique ») peut référer à :
Ce type de magic number est apparu dans les premières versions du code source de la version 7 d'Unix (en). Bien qu'il ait perdu son sens originel, le terme a subsisté dans le lexique de l'informatique.
Quand Unix fut porté sur le premier DEC PDP-11/20s, celui-ci n'avait pas de mécanisme de protection de la mémoire (en) et utilisait des références mémoires re-allouables (en). Ainsi, les versions avant la version 6 d'Unix (en) lisent un fichier exécutable dans la mémoire en sautant à l'offset 0. Avec le développement de la pagination, les versions suivantes d'Unix ont vu le développement des headers précisant les composants d'un fichier exécutable. Une instruction de saut placée au début du header a été developpée pour permettre d'exécuter le programme directement (sans lire le header) ; ce qui permet de choisir entre exécuter le programme en utilisant l'ancien mode utilisant des références mémoires réallouables (regular mode) ou en passant par la pagination. Avec le développement des formats d'exécutables, de nouvelles constantes de saut ont été ajoutées en incrémentant l'offset.
Dans le Lions' Commentary on UNIX 6th Edition, with Source Code (en) de la version 6 d'Unix, la fonction exec() lit l'image binaire d'un exécutable à partir du système de fichier. Les huit premiers octets forment le header qui contient la taille du programme (segment text) et les variables initialisées (segment global). Le premier mot de seize bits de ce header est comparé à deux constantes pour déterminer si l'exécutable utilise des références mémoires réallouables, le système de page en lecture seule récemment développé ou des pages séparées pour les instructions et les données. Dans les sixième et septième versions d'Unix, le double rôle de cette constante du début du header n'était pas précisé mais le bit de poids fort de cette constant était l'opcode de l'instruction de saut sur un PDP-11 (octal 000407 ou hex 0107). Si on ajoute sept au compteur de programme d'un programme exécuté, celui-ci va utiliser le service exec() pour se lancer.
Le service exec() lit le header du fichier exécutable (méta) depuis un buffer de l'espace noyau mais l'image exécutable est lue dans l'espace utilisateur et donc sans pouvoir utiliser la constante de saut. Les magic number ont alors été implémentés dans l'éditeur de liens et le chargeur d'Unix ; ils ont du être utilisés par la suite dans les programmes de test livrés avec les versions 6 et 7 d'Unix.
Dans la version 7, la constante n'est pas lue directement ; elle est d'abord assignée à la variable ux_mag et fut par la suite désignée par l'expression magic number. Sachant qu'il y avait alors dans cet Unix environ 10 000 lignes de code et beaucoup de constantes utilisées, ce nom est plutôt curieux pour une constante, au moins autant que le commentaire laissé dans la partie concernant le changement de contexte de la version 6 du gestionnaire d'applications d'Unix. C'est probablement pour cela que le terme a ensuite désigné le type d'exécutable, puis étendu aux systèmes de fichiers et étendu encore pour désigner un exécutable utilisant un typage fort.
Plusieurs de ces nombres sont issus d'un typage fort des données ou de leur multiplexage. Ils permettent aux programmes traitant l'information d'identifier les données qui suivent et notamment de distinguer le format de données utilisé.
Sous unix, la commande file permet de repérer le format d'un fichier à partir d'une signature. Il existe plusieurs projets tentant de les énumérer.
Le terme de magic number peut également correspondre à l'utilisation de constantes numériques non-nommées dans le code source d'un programme. L'utilisation de ces constantes viole les anciennes règles de programmation issues de COBOL, de FORTRAN et de PL/1, ne rend pas plus claire le choix de cette valeur et provoque généralement des erreurs de programmation. Le nommage de tous les constantes rend le code plus lisible, plus compréhensible et plus facilement maintenable.
Le nom des constantes doit avoir un sens selon le contexte ; par exemple, il vaut mieux éviter les codes du genre SEIZE = 32 alors que [NOMBRE](/fr/definitions/nombre)[type=embedded-definition]_DE_BITS aurait été plus clair.
Les problèmes avec ces magic number ne se limitent pas au constantes numériques ; le terme est également utilisé pour d'autres types de données, la déclaration des constantes étant plus flexible et porteuse de sens. Ainsi, déclarer const string testNomUtilisateur = "Jean" est meilleur que d'utiliser le mot-clé "Jean" de manière disséminée dans le programme ; de plus, cela simplifie la phase de tests.
Par exemple, le pseudo-code suivant permet de mélanger aléatoirement les valeurs d'un tableau représentant un jeu de 52 cartes :
for i from 1 to 52 j := i + randomInt(53 - i) - 1 jeu.swapEntries(i, j)
où jeu est un objet de type tableau, la fonction randomInt(x) choisit un chiffre au hasard entre 1 et x compris et swapEntries(i, j) échange la position des données placées en i et j dans le tableau. Dans cet exemple, 52 est un magic number. Il est plutôt conseillé d'écrire :
constant int nombreDeCarte := 52 for i from 1 to nombreDeCarte j := i + randomInt(nombreDeCarte + 1 - i) - 1 jeu.swapEntries(i, j)
Et ce pour plusieurs raisons :
function melange(int nombreDeCarte ) for i from 1 to nombreDeCarte j := i + randomInt(nombreDeCarte + 1 - i) - 1 jeu.swapEntries(i, j)
Il y a tout de même quelques défauts :
Dans certains cas – dépendant des habitudes de codage –, l'utilisation de constantes numériques non-nommées est acceptée :
Les constantes 1 et 0 sont parfois utilisées pour représenter les valeurs booléennes « Vraie » et « Faux » dans les langages de programmation qui n'ont pas ce type (comme les anciennes versions de C).
En C/C++, 0 est parfois utilisé pour représenter un pointer ou référence null. Comme pour les valeurs booléens, les librairies de C standards contiennent des macro-définition de NULL dont l'utilisation est fortement conseillée. D'autres langages proposent des valeurs null ou nil spécifiques.
Il est possible de créer ou modifier des GUID pour qu'ils soient facilement mémorisables bien que cela puisse affecter leur efficacité et leur unicité. Les règles pour générer des GUID et des UUID sont complexes mais assurent d'avoir des numéros uniques si elles sont suivies scrupuleusement.
Plusieurs GUID java commencent par « CAFEEFAC ».
Certains magic number ont une valeur particulière qui est écrite dans la mémoire vive pendant l'allocation dynamique (en) ou la ré-allocation pour permettre un débugage en cas de plantage. Pendant le débugage, la mémoire est généralement affichée en héxadécimal où des codes répétitifs ou en Hexspeak (en) sont facilement reconnaissables.
Ces valeurs sont utilisées de préférence sur des processeurs sans bit d'adressage car ceux-ci planteront s'ils essaient de les utiliser comme pointeur. De même, elles ne doivent pas faire partie du jeu d'instructions.
Vu qu'il soit rare qu'un entier codé sur 32 bit prenne une de ses valeurs, le passage d'un de ces nombres dans un débbugeur ou un core dump (en) indique généralement un dépassement de tampon ou une variable non initialisée.
| Code | Description |
|---|---|
| ..FACADE | Utilisé par beaucoup de systèmes d'exploitation temps réel |
| 8BADF00D | Utilisé par Apple comme code d'exception dans l'iPhone quand une application a pris trop de temps pour se lancer ou se terminer |
| A5A5A5A5 | Utilisé sur les systèmes embarqués car la séquence binaire correspondante (10100101) est facilement reconnaissable sur un oscilloscope ou un analyseur logique |
| ABABABAB | Utilisé par la fonction HeapAlloc() de Microsoft pour marquer le « no man's land » d'un Guard byte (en) après l'allocation de la mémoire heap |
| ABADBABE | Utilisé par Apple comme « Boot Zero Block » |
| ABADCAFE | Valeur d'initialisation utilisée pour démasquer les pointeurs brisés |
| BAADF00D | Utilisé par la fonction LocalAlloc(LMEM_FIXED) de Microsoft pour marquer la mémoire heap allouée mais non initialisée |
| BADBADBADBAD | Utilisé sur les calculateurs de Burroughs Corporation pour repérer la mémoire non-initialisée (mot de 48-bit) |
| BADC0FFEE0DDF00D | Utilisé sur le système 64 bits RS/6000 pour indiquer les registres processeurs non-initialisés |
| BADCAB1E | Code d'erreur retourné par le debuggeur eVC quand la connexion avec le débuggeur est coupée |
| BADDCAFE | Sous Solaris, marque la mémoire du noyau qui n'est pas initialisée (KMEM_UNINITIALIZED_PATTERN) |
| BEEFCACE | Utilisé sur le framework .NET comme nombre magique pour les fichiers de ressources (en) |
| C0DEDBAD | Utilisé pour le débuggage des tables MMU. |
| CAFEBABE | Dans les exécutables Mach-O (Fat binary (en) dans les processeurs 68k et les PowerPC) pour identifier les fichiers objet et les .class java |
| CAFEFEED | Sous Solaris, marque la mémoire allouée par la fonction kmemfree() pour le debugage |
| CCCCCCCC | Utilisé par la libraire de debugage du C++ de Microsoft pour repérer la mémoire stack non-initialisée |
| CDCDCDCD | Utilisé par la libraire de debugage du C++ de Microsoft pour repérer la mémoire heap non-initialisée |
| CEFAEDFE | Peut-être vu dans les binaires Mach-O de Mac OS X (voir FEEDFACE) |
| DDDDDDDD | Utilisé par MicroQuill's SmartHeap et le débuggeur mémoire C++ de Microsoft pour marquer la mémoire heap libérée |
| DEADBABE | Marque le début des fichiers arena d'IRIX |
| DEADBEEF | Connu pour être utilisé sur les systèmes IBM (notamment sur RS/6000), le premier Mac OS (OPENSTEP) et sur le Commodore Amiga. Sous Solaris, marque la mémoire du noyau libérée (KMEM_FREE_PATTERN) |
| DEADDEAD | Le code d'erreur STOP de Windows utilisé quand l'utilisateur crash volontairement la machine |
| DEADF00D | Marque toutes les nouvelles zones mémoires allouées quand elles n'ont pas été explicitement nettoyées après une corruption |
| DEADFA11 | Utilisé par Apple comme code d'exception dans l'iPhone quand l'utilisateur a forcé une application à s'éteindre |
| EBEBEBEB | Pour MicroQuill's SmartHeap |
| FADEDEAD | Marque le fin des scripts AppleScript |
| FDFDFDFD | Utilisé par le débuggeur mémoire C++ de Microsoft pour marquer le « no man's land » d'un Guard byte (en) avant et après la mémoire heap allouée |
| FEE1DEAD | Utilisé par l'appel système reboot() de Linux |
| FEEDFACE | Peut-être vu sur le binaire PowerPC Mach-O de Mac OS X. Sous Solaris, marque la zone rouge (KMEM_REDZONE_PATTERN) |
| FEEEFEEE | Utilisé par la fonction HeapFree() de Microsoft pour marquer la mémoire heap libérée |
La prépondérance de valeur issue de technologies de Microsoft ; elles sont largement commentées dans le livre de Steve Maguire (en) Writing Solid Code (en). Il donne plusieurs critères pour les choisir :
Comme elles sont souvent utilisées pour identifier des zones mémoires quasiment vides, elles tendent parfois à être utilisées dans le langage courant dans le sens « perdu, abandonné, vidé de la mémoire » : par exemple, « Ton programme est DEADBEEF ».