En programación de computadoras , un número mágico es cualquiera de los siguientes:
El término número mágico o constante mágica se refiere al antipatrón de usar números directamente en el código fuente. Se ha dicho que esto es romper una de las reglas de programación más antiguas, que se remonta a los manuales COBOL , FORTRAN y PL/1 de la década de 1960. [1] El uso de números mágicos sin nombre en el código oscurece la intención de los desarrolladores al elegir ese número, [2] aumenta las oportunidades de errores sutiles (por ejemplo, ¿es correcto cada dígito en 3.14159265358979323846 y es igual a 3.14159? [3] ) y hace más difícil que el programa se adapte y amplíe en el futuro. [4] Reemplazar todos los números mágicos significativos con constantes con nombre (también llamadas variables explicativas) hace que los programas sean más fáciles de leer, comprender y mantener. [5]
Los nombres elegidos para que sean significativos en el contexto del programa pueden dar como resultado un código que sea más fácil de entender para un mantenedor que no sea el autor original (o incluso para el autor original después de un período de tiempo). [6] Un ejemplo de una constante con un nombre no informativo es int SIXTEEN = 16
, aunque int NUMBER_OF_BITS = 16
es más descriptivo.
Los problemas asociados con los 'números' mágicos descritos anteriormente no se limitan a los tipos numéricos y el término también se aplica a otros tipos de datos donde declarar una constante con nombre sería más flexible y comunicativo. [1] Por lo tanto, declarar const string testUserName = "John"
es mejor que varias apariciones del 'valor mágico' "John"
en un conjunto de pruebas .
Por ejemplo, si es necesario barajar aleatoriamente los valores en una matriz que representa una baraja de naipes estándar , este pseudocódigo hace el trabajo utilizando el algoritmo de barajado de Fisher-Yates :
para i del 1 al 52 j := i + aleatorioInt(53 - i) - 1 a.swapEntries(i, j)
donde a
es un objeto de matriz, la función randomInt(x)
elige un número entero aleatorio entre 1 y x , inclusive, e swapEntries(i, j)
intercambia las entradas i y j en la matriz. En el ejemplo anterior, 52
es un número mágico. Se considera un mejor estilo de programación escribir lo siguiente:
int deckSize:= 52 para i de 1 a deckSize j := i + randomInt(tamañodecubierta + 1 - i) - 1 a.swapEntries(i, j)
Esto es preferible por varias razones:
deckSize
variable en el segundo ejemplo sería un cambio simple de una línea.dekSize
" en lugar de " deckSize
" daría como resultado una advertencia del compilador que dekSize
no está declarada.deckSize
en un parámetro de ese procedimiento, mientras que el primer ejemplo requeriría varios cambios.función aleatoria ( int deckSize) para i de 1 a deckSize j := i + randomInt(tamañodecubierta + 1 - i) - 1 a.swapEntries(i, j)
Las desventajas son:
deckSize + 1
en tiempo de ejecución que el valor "53", aunque la mayoría de los compiladores e intérpretes modernos notarán que deckSize
se ha declarado como una constante y calcularán previamente el valor 53 en el código compilado. Incluso cuando eso no sea una opción, la optimización del bucle moverá la suma para que se realice antes del bucle. Por lo tanto, normalmente no hay (o es insignificante) una penalización de velocidad en comparación con el uso de números mágicos en el código. Especialmente el costo de la depuración y el tiempo necesario para tratar de comprender el código no explicativo deben compararse con el pequeño costo de cálculo.En algunos contextos, el uso de constantes numéricas sin nombre es generalmente aceptado (y posiblemente "no es mágico"). Si bien dicha aceptación es subjetiva y a menudo depende de los hábitos de codificación individuales, los siguientes son ejemplos comunes:
for (int i = 0; i < max; i += 1)
isEven = (x % 2 == 0)
, ¿dónde %
está el operador de módulo ?circumference = 2 * Math.PI * radius
, [1] o para calcular el discriminante de una ecuación cuadrática comod = b^2 − 4*a*c
(f(x) ** 2 + f(y) ** 2) ** 0.5
forLas constantes 1 y 0 se utilizan a veces para representar los valores booleanos Verdadero y Falso en lenguajes de programación sin un tipo booleano , como las versiones anteriores de C. La mayoría de los lenguajes de programación modernos proporcionan un tipo primitivoboolean
o , por lo que no se recomienda el uso de 0 y 1. Esto puede resultar más confuso ya que 0 a veces significa éxito programático (cuando -1 significa fracaso) y fracaso en otros casos (cuando 1 significa éxito).bool
En C y C++, 0 representa el puntero nulo . Al igual que con los valores booleanos, la biblioteca estándar de C incluye una definición de macro NULL
cuyo uso se recomienda. Otros lenguajes proporcionan un valor específico null
y nil
cuando este es el caso no se debe utilizar ninguna alternativa. La constante de puntero escrita nullptr
se introdujo con C++ 11.
Los indicadores de formato se utilizaron por primera vez en el código fuente de Unix de la versión 7 anterior . [ cita necesaria ]
Unix fue portado a uno de los primeros DEC PDP-11 /20, que no tenía protección de memoria . Por eso, las primeras versiones de Unix utilizaban el modelo de referencia de memoria reubicable . [7] Las versiones Unix anteriores a la sexta edición leen un archivo ejecutable en la memoria y saltan a la primera dirección de memoria baja del programa, la dirección relativa cero. Con el desarrollo de versiones paginadas de Unix, se creó un encabezado para describir los componentes de la imagen ejecutable . Además, se insertó una instrucción de bifurcación como primera palabra del encabezado para omitir el encabezado e iniciar el programa. De esta manera, un programa podría ejecutarse en el modo (normal) de referencia de memoria reubicable anterior o en modo paginado. A medida que se desarrollaron más formatos ejecutables, se agregaron nuevas constantes incrementando el desplazamiento de la rama . [8]
En el código fuente de la sexta edición del cargador de programas Unix, la función exec() lee la imagen ejecutable ( binaria ) del sistema de archivos. Los primeros 8 bytes del archivo eran un encabezado que contenía los tamaños del programa (texto) y las áreas de datos inicializadas (globales). Además, la primera palabra de 16 bits del encabezado se comparó con dos constantes para determinar si la imagen ejecutable contenía referencias de memoria reubicables (normal), la imagen ejecutable paginada de solo lectura recién implementada o la imagen paginada de instrucciones y datos separados. [9] No se mencionó la doble función de la constante de encabezado, pero el byte de orden superior de la constante era, de hecho, el código de operación para la instrucción de bifurcación PDP-11 ( octal 000407 o hexadecimal 0107). Agregar siete al contador del programa mostró que si se ejecutaba esta constante , bifurcaría el servicio Unix exec() sobre el encabezado de ocho bytes de la imagen ejecutable e iniciaría el programa.
Dado que la sexta y séptima ediciones de Unix empleaban código de paginación, la doble función de la constante de encabezado estaba oculta. Es decir, el servicio exec() lee los datos del encabezado ( meta ) del archivo ejecutable en un búfer de espacio del kernel , pero lee la imagen ejecutable en el espacio del usuario , por lo que no utiliza la función de ramificación de la constante. La creación de números mágicos se implementó en el enlazador y cargador de Unix y la ramificación de números mágicos probablemente todavía se usaba en el conjunto de programas de diagnóstico independientes que venían con las ediciones sexta y séptima. Por lo tanto, la constante del encabezado proporcionó una ilusión y cumplió con los criterios de magia .
En la versión siete de Unix, la constante del encabezado no se probó directamente, sino que se asignó a una variable denominada ux_mag [10] y posteriormente se la denominó número mágico . Probablemente debido a su singularidad, el término número mágico pasó a significar tipo de formato ejecutable, luego se expandió para significar tipo de sistema de archivos y se expandió nuevamente para significar cualquier tipo de archivo.
Los números mágicos son comunes en programas de muchos sistemas operativos. Los números mágicos implementan datos fuertemente tipados y son una forma de señalización dentro de banda para el programa de control que lee los tipos de datos en tiempo de ejecución del programa. Muchos archivos tienen constantes que identifican los datos contenidos. Detectar dichas constantes en archivos es una forma sencilla y eficaz de distinguir entre muchos formatos de archivos y puede proporcionar más información en tiempo de ejecución .
CAFEBABE
. Cuando se comprime con Pack200, los bytes se cambian a CAFED00D
.47
49
46
38
39
61
) o "GIF87a" ( 47
49
46
38
37
61
)FF
D8
y terminan con FF
D9
. Los archivos JPEG/ JFIF contienen la cadena terminada en nulo "JFIF" ( 4A
46
49
46
00
). Los archivos JPEG/ Exif contienen la cadena terminada en nulo "Exif" ( 45
78
69
66
00
), seguida de más metadatos sobre el archivo.89
50
4E
47
0D
0A
1A
0A
). Esa firma contiene varios caracteres de nueva línea para permitir detectar conversiones de nueva línea automáticas injustificadas, como la transferencia del archivo mediante FTP con el modo de transferencia ASCII en lugar del modo binario . [11]4D
54
68
64
23
21
) seguido de la ruta a un intérprete , si es probable que el intérprete sea diferente de aquel desde el cual se invocó el script.7F
seguido de "ELF" ( 7F
45
4C
46
).25
21
).25
50
44
46
).4D
5A
), las iniciales del diseñador del formato de archivo, Mark Zbikowski . La definición permite el poco común "ZM" ( 5A
4D
) así como para dosZMXP, un EXE que no es PE. [12]19
54
01
19
o 01
19
54
según la versión; ambos representan el cumpleaños del autor, Marshall Kirk McKusick .55
AA
4A
6F
79
21
) como prefijo.49
49
2A
00
. "MM" es para Motorola, que utiliza el orden de bytes big endian , por lo que el número mágico es 4D
4D
00
2A
.FE
FF
para big endian y FF
FE
little endian). Y en Microsoft Windows , los archivos de texto UTF-8 suelen comenzar con la codificación UTF-8 del mismo carácter, EF
BB
BF
.42
43
).D0
CF
11
E0
) comienzan con , que sugiere visualmente la palabra "DOCFILE0".50
4B
03
04
), donde "PK" son las iniciales de Phil Katz , autor de la utilidad de compresión de DOS PKZIP .37
7A
BC
AF
27
1C
.El programa de utilidad Unix file
puede leer e interpretar números mágicos de archivos, y el archivo que se utiliza para analizar la información se llama magic . La utilidad TrID de Windows tiene un propósito similar.
2A
.52
46
42
, para "Remote Frame Buffer") seguido del número de versión del protocolo del cliente.FF
53
4D
42
', o "\xFFSMB"
al inicio de la solicitud SMB.05
el inicio de la solicitud (que representa Microsoft DCE/RPC versión 5), seguido inmediatamente por un 00
o 01
para la versión secundaria. En las solicitudes MSRPC basadas en UDP, el primer byte siempre es 04
.4D
45
4F
57
). Las extensiones de depuración (utilizadas para la conexión de canales DCOM) están precedidas por la secuencia de bytes "MARB" ( 4D
41
52
42
).19
que representa la longitud del encabezado, seguido inmediatamente por la frase "protocolo BitTorrent" en la posición 1 del byte.E3
representa un cliente eDonkey, C5
representa eMule y D4
representa eMule comprimido.0xD9B4BEF9
, que indica la red principal, mientras que la constante 0xDAB5BFFA
indica la red de prueba.80
y una respuesta del servidor SSLv3 a un saludo de cliente comienza con 16
(aunque esto puede variar).0x63
0x82
0x53
0x63
' al inicio de la sección de opciones del paquete. Este valor está incluido en todos los tipos de paquetes DHCP.0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a
' o " PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
". El prefacio está diseñado para evitar el procesamiento de tramas por parte de servidores e intermediarios que soportan versiones anteriores de HTTP pero no 2.0.Los números mágicos son comunes en las funciones e interfaces API de muchos sistemas operativos , incluidos DOS , Windows y NetWare :
0000
para 1234
decidir si el sistema debe contar con memoria o no al reiniciar, realizando así un arranque en frío o en caliente. Estos valores también los utilizan los administradores de memoria EMM386 que interceptan solicitudes de arranque. [13] Las BIOS también utilizan valores mágicos 55 AA
para determinar si un disco es de arranque. [14]Esta es una lista de límites de los tipos de almacenamiento de datos: [16]
Es posible crear o alterar identificadores únicos globalmente (GUID) para que sean memorables, pero esto se desaconseja porque compromete su fuerza como identificadores casi únicos. [17] [18] Las especificaciones para generar GUID y UUID son bastante complejas, lo que los lleva a ser prácticamente únicos, si se implementan correctamente. . [ cita necesaria ]
Los números de identificación de producto de Microsoft Windows para los productos de Microsoft Office a veces terminan en 0000-0000-0000000FF1CE
("OFFICE"), como { 90160000-008C-0000-0000-0000000FF1CE
}, el ID de producto para el "Componente de extensibilidad hacer clic y ejecutar de Office 16".
Java utiliza varios GUID que comienzan con CAFEEFAC
. [19]
En la tabla de particiones GUID del esquema de partición GPT, las particiones de arranque del BIOS utilizan el GUID especial { 21686148-6449-6E6F-744E-656564454649
} [20] que no sigue la definición de GUID; en cambio, se forma utilizando los códigos ASCII para la cadena " Hah!IdontNeedEFI
" parcialmente en orden little endian . [21]
Los valores de depuración mágica son valores específicos escritos en la memoria durante la asignación o desasignación, de modo que luego será posible saber si se han dañado o no y hacer obvio cuándo se están utilizando valores tomados de la memoria no inicializada. La memoria generalmente se ve en hexadecimal, por lo que son comunes los valores repetidos o de jerga hexadecimal memorables . Se pueden preferir valores numéricamente impares para que los procesadores sin direccionamiento de bytes fallen al intentar usarlos como punteros (que deben caer en direcciones pares). Se deben elegir valores que estén alejados de direcciones probables (el código del programa, datos estáticos, datos del montón o la pila). De manera similar, se pueden elegir de manera que no sean códigos válidos en el conjunto de instrucciones para la arquitectura dada.
Dado que es muy poco probable, aunque posible, que un entero de 32 bits tome este valor específico, la aparición de dicho número en un depurador o volcado de memoria probablemente indique un error como un desbordamiento del búfer o una variable no inicializada .
Ejemplos famosos y comunes incluyen:
La mayoría de ellos tienen una longitud de 32 bits cada uno , el tamaño de palabra de la mayoría de las computadoras con arquitectura de 32 bits.
La prevalencia de estos valores en la tecnología de Microsoft no es una coincidencia; se analizan en detalle en el libro de Steve Maguire Writing Solid Code de Microsoft Press . Da una variedad de criterios para estos valores, tales como:
Dado que a menudo se usaban para marcar áreas de la memoria que estaban esencialmente vacías, algunos de estos términos llegaron a usarse en frases que significaban "desaparecido, abortado, borrado de la memoria"; por ejemplo, "Su programa es DEADBEEF". [ cita necesaria ]