Juego de palabras

La versión actual de la página aún no ha sido revisada por colaboradores experimentados y puede diferir significativamente de la versión revisada el 19 de octubre de 2017; las comprobaciones requieren 11 ediciones .

Juego de palabras es un término  utilizado en informática para referirse a varias técnicas para violar o engañar al sistema de tipos de un lenguaje de programación , con un efecto que sería difícil o imposible de proporcionar dentro de un lenguaje formal .

Los lenguajes C y C++ proporcionan juegos de palabras explícitos a través de construcciones como casts , y también para C++ , aunque los estándares de esos lenguajes tratan algunos casos de tales juegos de palabras como un comportamiento indefinido . unionreinterpret_cast

En Pascal , las notaciones variantes se pueden usar para interpretar un tipo de datos en particular en más de una forma, o incluso de una forma que no es nativa del idioma.

Escribir un juego de palabras es una violación directa de la seguridad tipográfica . Tradicionalmente, la capacidad de crear un juego de palabras se asocia con una escritura débilunsafe , pero algunos lenguajes fuertemente tipeados o sus implementaciones brindan tales capacidades (generalmente mediante el uso de palabras o en sus identificadores asociados unchecked). Los defensores de la seguridad tipográfica argumentan que la " necesidad " de escribir juegos de palabras es un mito [1] .

Ejemplo: enchufes

Un ejemplo clásico de un juego de palabras se puede ver en la interfaz de socket de Berkeley . Una función que vincula un socket abierto no inicializado a una dirección IP tiene la siguiente firma:

int bind ( int sockfd , struct sockaddr * my_addr , socklen_t addrlen );

La función bindse suele llamar así:

estructura sockaddr_insa = { 0 } ; int calcetinesfd = ...; . sin_familia = AF_INET ; . sin_port = htons ( puerto ); bind ( sockfd , ( struct sockaddr * ) & sa , sizeof sa );

La biblioteca de sockets de Berkeley básicamente se basa en el hecho de que, en C, un puntero a struct sockaddr_inse puede convertir fácilmente en un puntero a struct sockaddr, y que los dos tipos de estructuras se superponen en su organización de memoria . Por lo tanto, un puntero a un campo (donde tiene tipo ) en realidad apuntará a un campo (donde tiene tipo ). En otras palabras, la biblioteca usa un juego de palabras para implementar una forma primitiva de herencia . [2]my_addr->sin_familymy_addr struct sockaddr*sa.sin_familysa struct sockaddr_in

En la programación, es común usar estructuras : "capas" que le permiten almacenar de manera efectiva varios tipos de datos en un solo bloque de memoria . En la mayoría de los casos, este truco se utiliza para datos mutuamente excluyentes con fines de optimización .

Ejemplo: números de coma flotante

Suponga que desea comprobar que un número de punto flotante es negativo. Uno podría escribir:

bool es_negativo ( flotante x ) { devuelve x < 0.0 ; }

Sin embargo, las comparaciones de punto flotante consumen muchos recursos porque funcionan de una manera especial para NaN . Dado que el tipo floatse representa de acuerdo con el estándar IEEE 754-2008 , y el tipo inttiene 32 bits de largo y tiene el mismo bit de signo que en , puede usar un juego de palabras para extraer el bit de signo de un número de coma flotante usando solo un número entero comparación: float

bool es_negativo ( flotante x ) { volver * (( int * ) & x ) < 0 ; }

Esta forma de juego de palabras es la más peligrosa. El ejemplo anterior se basó únicamente en las garantías dadas por el lenguaje C con respecto a la representación de estructuras y la convertibilidad de punteros ; sin embargo, este ejemplo se basa en supuestos de hardware específicos . En algunos casos, como cuando se desarrollan aplicaciones en tiempo real que el compilador no es capaz de optimizar por sí mismo, estas peligrosas decisiones de programación resultan necesarias. En tales casos, los comentarios y las comprobaciones en tiempo de compilación ( Static_assertions ) ayudan a garantizar la capacidad de mantenimiento del código . 

Puede encontrar un ejemplo real en el código de Quake III ; consulte Raíz cuadrada inversa rápida .

Además de las suposiciones sobre la representación bit a bit de números de punto flotante, el ejemplo anterior de un juego de palabras también viola las reglas establecidas por el lenguaje C para acceder a objetos [3] : xse declara como float, pero su valor se lee en un expresión que tiene tipo signed int . En muchas plataformas comunes, este juego de palabras con punteros puede generar problemas si los punteros están alineados de manera diferente en la memoria . Además, los punteros de diferentes tamaños pueden compartir las mismas ubicaciones de memoria , lo que genera errores que el compilador no puede detectar .

Usando unión

El problema del aliasing se puede resolver usando union(aunque el siguiente ejemplo se basa en la suposición de que el número de coma flotante está representado por el estándar IEEE-754 ):

bool es_negativo ( flotante x ) { unión { sin firmar int ui ; flotador d ; } mi_sindicato = { . d = x }; volver ( my_union . ui & 0x80000000 ) ! = 0 ; }

Este es un código C99 que utiliza inicializadores designados .  Cuando se crea una unión , se inicializa su campo real y luego se lee el valor de todo el campo (ubicado físicamente en la misma dirección en la memoria), de acuerdo con la cláusula s6.5 del estándar. Algunos compiladores admiten construcciones como una extensión del lenguaje, como GCC [4] .

Para ver otro ejemplo de un juego de palabras, consulte Stride of an array   .

Pascual

La notación de variante le permite considerar el tipo de datos de diferentes maneras, según la variante especificada. El siguiente ejemplo asume integer16 bits longinty real32 bits y character8 bits:

type variant_record = record case rec_type : longint of 1 : ( I : array [ 1 .. 2 ] of integer ) ; 2 : ( L : entero largo ) ; 3 : ( R : real ) ; 4 : ( C : arreglo [ 1 .. 4 ] de carácter ) ; fin ; Var V : registro_variante ; K : Entero ; L.A .: Entero largo ; RA : Real ; Ch : carácter ; ... V . yo := 1 ; Ch := V . C [ 1 ] ; (* Obtiene el primer byte del campo VI *) V . R := 8.3 ; LA := V . L ; (*Almacenar número real en celda entera*)

En Pascal , copiar un real a un entero lo convierte en un valor redondeado. Este método, sin embargo, convierte un valor de punto flotante binario en algo que tiene la longitud de un entero largo (32 bits), que no es idéntico e incluso puede ser incompatible con los enteros largos en algunas plataformas.

Dichos ejemplos se pueden usar para transformaciones extrañas; sin embargo, en algunos casos, tales construcciones pueden tener sentido, por ejemplo, al calcular la ubicación de ciertos datos. El siguiente ejemplo asume que el puntero y el entero largo son de 32 bits:

Tipo PA = ^ Arec ; Arec = caso de registro rt : entero largo de 1 : ( P : PA ) ; 2 : ( L : Entero largo ) ; fin ; Var PP : PA ; K : entero largo ; ... Nuevo ( PP ) ; PP ^. P := PP ; Writeln ( 'La variable PP se encuentra en memoria en ' , hex ( PP ^. L )) ;

El procedimiento estándar Newen Pascal está destinado a asignar memoria dinámicamente para un puntero y hexestá implícito en algún procedimiento que imprime una cadena hexadecimal que describe el valor de un número entero. Esto le permite mostrar la dirección del puntero, que generalmente está prohibido (los punteros en Pascal no se pueden leer ni generar, solo asignar). Asignar un valor a una variante entera de un puntero le permite leer y modificar cualquier área de la memoria del sistema:

PP ^. L : = 0 PP := PP ^. pag ; (*PP apunta a la dirección 0*) K := PP ^. L ; (* K contiene el valor de la palabra en la dirección 0 *) Writeln ( 'La palabra en la dirección 0 de esta máquina contiene' , K ) ;

Este programa puede funcionar correctamente o bloquearse si la dirección 0 está protegida contra lectura, según el sistema operativo.

Véase también

Notas

  1. Lawrence C. Paulson. ML para el programador de trabajo. — 2do. - Cambridge, Gran Bretaña: Cambridge University Press, 1996. - S. 2. - 492 p. - ISBN 0-521-57050-6 (tapa dura), 0-521-56543-X (tapa blanda).
  2. estructura sockaddr_in, estructura in_addr . www.gta.ufrj.br. Fecha de acceso: 17 de enero de 2016. Archivado desde el original el 24 de enero de 2016.
  3. ISO/IEC 9899:1999 s6.5/7
  4. GCC: Sin errores . Consultado el 21 de noviembre de 2014. Archivado desde el original el 22 de noviembre de 2014.

Enlaces