- Magic number
-
Magic number
En programmation informatique, le terme magic number (en français « nombre magique ») peut référer à :
- une constante numérique ou un ensemble de caractères utilisé pour désigner un format de fichier ou un protocole ;
- une constante numérique non-nommée ou non-documentée ;
- un ensemble de valeurs ayant un sens particulier (par exemple, les GUID).
Sommaire
Indicateur de format
Origine
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)[1]. 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é 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 re-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[2].
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 re-allouables, le système de page en lecture seul récemment développé ou des pages séparées pour les instructions et les données[3]. Dans la sixième et la septième version 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 serviceexec()
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 lut dans l'espace utilisateur et donc sans pouvoir utiliser la constante de saut. Les magic number ont alors été implémenté dans l'éditeur de liens et le chargeur d'Unix ; ils ont du être utilisé par la suite dans les programmes de test livrés avec la version 6 et 7 d'Unix.Dans la version 7, la constante n'est pas lu directement ; elle est d'abord assignée à la variable
ux_mag
[4] et fut par la suite désigné 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 plustôt curieux pour une constante, au moins autant que le commentaire[1] 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.Dans les fichiers
Exemples
Détection
Dans les protocoles
Constante numérique innommée
Le terme de magic number peut également correspondre à l'utilisation de constantes numériques innomées directement dans le code source d'un programme. L'utilisation de constantes numériques innomées dans le code source est la plupart du temps source d'erreur, et rend plus difficile l'adaptation ou la compréhension future d'un programme. Le nommage de tous les magic number rend le code plus lisible, plus compréhensible et plus facilement maintenable.
Les noms de constante choisis doivent avoir du sens dans le contexte précis.
Les problèmes avec les magic number décrits plus haut ne se limitent pas au constantes numériques, le terme est également utilisé pour d'autre type de données, la déclaration des constantes étant plus flexible et plus communicatif. Par exemple, déclarer
const string testNomUtilisateur = "Jean"
est meilleur que plusieurs occurrences du magic number"Jean"
dans une suite de tests.Par exemple, si il est nécessaire de mélanger aléatoirement les valeurs d'un tableau contenant les valeurs d'un jeu de 52 cartes, le pseudo code suivant le permettrait:
for i from 1 to 52 j := i + randomInt(53 - i) - 1 a.swapEntries(i, j)
où
a
est un objet de type tableau, la fonctionrandomInt(x)
choisi un chiffre au hasard entre 1 et x compris, etswapEntries(i, j)
échange la position des données placées en i et j dans le tableau. Dans l'exemple précédent,52
est un magic number. Il est conseillé de plutôt écrire:constant int nombreDeCarte := 52 for i from 1 to nombreDeCarte j := i + randomInt(nombreDeCarte + 1 - i) - 1 a.swapEntries(i, j)
Ceci est préférable pour plusieurs raisons:
- Le second exemple est plus facile à lire et à comprendre. Un développeur lisant pour le premier exemple se demanderait A quoi correspond le nombre 52? Pourquoi 52. Il serait certainement capable de comprendre la signification du 52 après la lecture du code complet, mais ce n'est pas simple. Les magic number deviennent particulièrement déroutant lorsque la même valeur est utilisé pour plusieurs signification dans une même partie de code.
- Il est plus simple de modifier la valeur de la constante dans le second exemple car celle-ci n'est pas dupliquée. Changer la valeur d'un magic number est source d'erreur, car la même valeur est très souvent utilisé plusieurs fois dans le même programme. Il se peut également, dans le cas où deux variables aux significations différentes ont la même valeur, qu'elle soient toutes deux modifiées accidentellement. Pour modifier le premier exemple afin de mélanger un jeu de tarot, qui contient 78 cartes, le développeur pourrait remplacer naïvement toutes les instances de 52 dans le programme par 78. Cela pourrait causer deux problèmes. Premièrement, la valeur 53 de la seconde ligne de l'exemple ne sera pas modifiée, ce qui ammènera le programme à ne traiter qu'une partie des cartes. Deuxièmement, il se peut qu'il remplace la chaîne "52" dans tout le programme sans prêter attention à ce qu'elle signifie, le nombre de cartes ou totalement autre chose, ce qui introduirait très certainement des bugs. Par comparaison, changer la valeur de
nombreDeCarte
dans le second exemple est une opération très simple et ne nécessite la modification que d'une seule ligne.
- La déclaration des variables remplaçant les magic number sont généralement placée ensemble, au début des fonctions ou fichier. Ceci facilite leur recherche et leur modification.
- La mise est paramètre est également plus aisée. Par exemple, pour transformer l'exemple ci-dessus en une procédure qui mélange un tas de carte, il sera aisé de passer
nombreDeCarte
en paramètre de la procédure. Le premier exemple nécessitera quant à lui plusieurs modifications, comme suit:
function melange(int nombreDeCarte )
for i from 1 to nombreDeCarte j := i + randomInt(nombreDeCarte + 1 - i) - 1 a.swapEntries(i, j)
- Il est également plus facile de détecter les erreurs de frappe. En utilisant une variable déclarée en lieu et place d'un literal permet l'utilisation de la vérification par le compilateur. Par exemple, saisir "62" au lieu de "52" ne sera pas détecté alors que "nombedeCarte" au lieu de "nombreDeCarte" lèvera un warning (ou une erreur) du compilateur nous informant que "nombeDeCarte" est indéfini.
- Cela permet également de minimiser les saisies au clavier dans certain IDE. Si l'IDE permet le complètement automatique, il suffira de saisir les premières lettres de la variable pour que l'IDE le complète automatiquement.
Incovénients:- Sur certain CPU, l'exécution de l'expression
nombreDeCarte + 1
prendra plus de temps que l'expression53
. Néanmoins, la plupart des compileurs et interpreteurs moderne sont capable de comprendre que la variablenombreDeCarte
a été déclarée comme constante et pre-calcule la valeur 53 dans le code compilé. Il n'y a donc aucun gain de temps à utiliser des magic number.
- L'utilisation de variables déclarée allonge les lignes de code, obligeant les lignes de code à s'étaler sur plusieurs lignes si plusieurs constantes sont utilisés sur la même ligne.
Usages acceptés comme constantes numériques non-nommées
Dans certain cas, l'utilisation de constantes numériques non-nommées est acceptée. Ces utilisations correctes étant subjectives, et dépendantes des habitudes de codage, les exemple suivant sont des exemples communs:
- l'utilisation du 0 ou du 1 comme valeur initiale ou valeur d'incrémentation dans les boucle for telle que
for (int i=0;i<max;i=i+1)
(en prenant comme hypothèse quei++
n'est pas supporté) - l'utilisation de 2 dans les expressions telles que
périmètre = rayon * Math.PI * 2
- l'utilisation de 2 pour vérifier qu'un nombre est pair ou impair
bool estPair = (x%2==0)
, où%
est l'opérateur modulo
Les constantes 1 et 0 sont parfois utilisées pour représentes les valeurs booléens True et False dans les langages de programmation sans type boolean comme les anciennes version de C. La plupart des languages modernes contiennent les types
boolean
oubool
, l'usage de 0 ou 1 dans ce cas là est proscrite.En C ou C++, 0 est parfois utilisé pour représenter le null pointer ou reference. Comme pour les valeurs booléens, les librairies de C standards contiennent des macro définitions de
NULL
dont l'utilisation est fortement conseillée. D'autre langages proposent des valeursnull
ounil
spécifiques et dans ce cas, aucune autre alternative ne devrait être utilisé.Magics GUIDs
Il est possible de créer ou modifier des GUID pour qu'il soit facilement mémorisables bien que cela pussi affecter leur efficacité eet leur unicité[5]. 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 suivi scrupuleusement.
Plusieurs GUID java commencent par «
CAFEEFAC
».Magic number de débogage
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 debugage 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 planterons s'ils essaient de les utiliser comme pointeur.
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 debbugeur ou un core dump (en) indique généralemnt un dépassement de tampon ou une variable non initialisée.
Valeurs classiques 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 heapABADBABE
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éeBADBADBADBAD
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 connection 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
CAFEBABE
Dans les exécutables Mach-O (Fat binary (en) dans les processeurs 68k et les PowerPC) pour identifier les fichier objet et les .class java CAFEFEED
Sous Solaris, marque le mémoire allouée par la fonction kmemfree()
pour le debugageCCCCCCCC
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émoire allouées quand elle 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 LinuxFEEDFACE
Peut-être vu sur le binaires 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éeNotes et références
- ↑ a et b Odd Comments and Strange Doings in Unix [1]
- ↑ Personal communication with Dennis M. Ritchie
- ↑ Version six system1 source file [2]
- ↑ Version seven system1 source file [3]
- ↑ (en) Joseph M. Newcomer, « Message Management - Guaranteeing uniqueness ». Mis en ligne le 13 octobre 2001, consulté le 6 septembre 2009
- Portail de la programmation informatique
Catégorie : Programmation informatique
Wikimedia Foundation. 2010.