AnteriorPosterior

Ampliación 6. Directivas del compilador (Turbo Pascal).

  Curso: Curso de Pascal, por Nacho Cabanes

Curso de Pascal. Ampliación 6. Directivas del compilador (Turbo Pascal).

Lo que vamos a ver a continuación no son órdenes del lenguaje Pascal, sino órdenes directas al compilador.  Son exclusivas de Turbo Pascal (aunque cada compilador suele tener las suyas propias, y es posible que coincidan). Estas órdenes nos permitirán indicarle al compilador cosas como:
 

  • Que incluya otro fichero justo en esa posición.
  • Que genere código para 286 o superior.
  • La cantidad de memoria (heap y pila) que queremos reservar.
  • Que no compile ciertas partes del programa.
  • [...]


Dividiremos las directivas en tres tipos:

  • Switches, que activan o desactivan una posibilidad.
  • Parametrizadas, que detallan características.
  • Condicionales.


Algunas de estas directivas ya las hemos mencionado en lecciones del curso.  Ahora iremos viendo cada uno de estos tipos por orden alfabético. Las que voy a tratar son las de Turbo Pascal 7.0 para DOS; en versiones anteriores pueden no existir todas, y en versiones posteriores (p . Borland Pascal 7.0, que compila también para DPMI y Windows) puede haber más.
 

Algunas directivas pueden activar como "switches" (interruptores), activando o desactivando ciertas características.  Estas son:

$A:  Alineación de datos.
$B:  Modo de evaluación Booleana.
$D:  Información para depuración.
$F:  Forzar llamadas lejanas (Far).
$G:  Generar código para 80286.
$I:  Comprobación de Entrada/salida.
$L:  Información de símbolos locales.
$N:  Coprocesador numérico 80x87 (y $E emulación).
$P:  Parámetros String abiertos.
$Q:  Comprobación de overflow.
$R:  Comprobación de rango.
$S:  Comprobación de desbordamiento de pila.
$T:  Comprobación de punteros con tipo.
$V:  Comprobación de Strings.
$X:  Sintaxis extendida.
 

 $A:  Alineación de datos.

Esta opción intercambia entre el alineamiento de byte y de doble byte para las variables.

Cuando se indica {$A+}, que es el valor por defecto, las variables de más de un byte de tamaño se colocan de modo que empiecen en una dirección de memoria par.

¿Para qué es esto?  Para conseguir mayor velocidad: en las CPUs 8086 y superiores (no en el 8088), se accede con más rapidez a las direcciones pares de memoria (un ciclo de reloj frente a dos).

Con {$A-} las variables no se alinean de ninguna forma, se colocan en la próxima posición libre de memoria, sea par o impar.
 

 $B:  Modo de evaluación Booleana.

Si se indica {$B+}, cuando haya dos comparaciones booleanas enlazadas por AND o por OR, se evalúa la expresión completa.

Si se usa {$B-}, que es el valor por defecto, la evaluación puede terminar antes si se conoce cual va a ser el resultado.  Por ejemplo, si una orden se debe ejecutar si ocurre una cosa Y otra (se deben dar dos condiciones simultaneas), y el compilador no se molesta en mirar la segunda si ya ve que la primera es falsa.
 

 $D:  Información para depuración.

La opción {$D+} guarda junto con el programa ejecutable una información adicional que nos permite hacerla funcionar paso a paso, a la vez que en pantalla se nos muestra la línea que se está ejecutando.  Esto nos ayuda a detectar errores con más facilidad.

Por defecto, esta información se guarda automáticamente.  Si queremos evitarlo, deberemos usar {$D-}.

Esta opción se suele usar junto con la de información sobre símbolos locales $L.
 

 $F:  Forzar llamadas lejanas (Far).

Afecta a la generación de código máquina:

Si se indica {$F+}, las llamadas a los procedimientos y funciones restantes se haran considerándolas como "far", es decir, suponiendo que se encuentran en otro segmento de 64K.  Esto es necesario cuando se emplean Overlays, por ejemplo.

El valor por defecto es {$F-}, en el que se considera que todas las procedimientos y funciones se encuentran en el mismo segmento, y se usan llamadas "cercanas" (near).

Cuando los procedimientos o funciones se encuentren en otra "unit", irán a un segmento distinto, pero no hace falta indicárselo al compilador, que lo tiene en cuenta automáticamente.
 

 $G:  Generar código para 80286.

El valor por defecto es {$G-}, que hace que sólo se empleen órdenes genéricas del 8086.

Con {$G+} permitimos al compilador que utilice algunas órdenes del 80286 para optimizar la generación de código, a cambio de que el programa no funcione en equipos XT.
 

 $I:  Comprobación de Entrada/salida.

Si está activada con {$I+}, que es el valor por defecto, y durante la ejecución del programa aparece algún error de entrada/salida (I/O), se interrumpe el programa y se muestra un mensaje de error.

Si la desactivamos con {$I-}, el programa no se aborta, sino que se devuelve un código de error en la variable IOResult, que nosotros debemos comprobar.

El uso más habitual es para comprobar si un fichero existe antes de intentar acceder a él, aunque también se puede usar para cosas más "sofisticadas", como comprobar si en un "readln" que esperaba un valor numérico hemos introducido otra cosa.
 

 $L:  Información de símbolos locales.

Hace que el compilador guarde información sobre las variables de un programa o unidad, para que podamos modificarlas en media sesión de depuración.  Por defecto, está activado ( {$L+} ).

Se suele usar en conjunción con $D.
 

 $N:  Coprocesador numérico 80x87.

Si se habilita con $N+, las operaciones con números reales se realizarán mediante el coprocesador matemático.  Estos además nos permite usar cuatro nuevos tipos numéricos: Single, Double, Extended, y Comp.

Si está deshabilitado (valor por defecto o con {$N-}), las operaciones con números reales se realizan por software, y no están disponibles estos 4 tipos.

Si queremos usarlos pero no tenemos coprocesador, existe la posibilidad de "emularlo" mediante software con la directiva {$E+}.

El uso habitual es {$N-,E-} en condiciones normales, o {$N+,E+} para usar estos tipos ampliados.  En este último caso, se emplea el coprocesador si existe, o se emula automáticamente si no es así.
 

 $P:  Parámetros String abiertos.

Con $P+, todos los parámetros de tipo String se consideran string abiertos (OpenString, ver $V).

Con {$P-} se tratan como en versiones anteriores de TP.
 

 $Q:  Comprobación de overflow.

Si está habilitado, se comprueba que no haya overflow (desbordamiento de la capacidad de un cierto tipo de datos) en el resultado de alguna de estas operaciones:

   +, -, * Abs, Sqr, Succ, Pred

Por ejemplo, si en una variable de tipo byte intentamos almacenar 200+200, que supera el valor máximo de 255, el programa se abortaría con un mensaje de error.

Usar {$Q+} hace el programa más grande y lento, así que conviene emplearlo sólo mientras se esté depurando, y no conservarlo en la versión definitiva.
 

 $R:  Comprobación de rango.

Comprueba que el valor asignado a una variable (o al índice de un array o un string) esté dentro del rango correcto.

Al igual que ocurre con $Q, conviene usarlo sólo mientras se esté depurando el programa.
 

 $S:  Comprobación de desbordamiento de pila.

Comprueba si la pila se desborda, y si es así, interrumpe el programa.

Como en la pila se almacenan las direcciones de retorno cuando se llama a una función o procedimiento, el desbordar la capacidad de la pila puede suponer que se lea un dato erróneo y se salte a una dirección indeterminada de la memoria, lo que puede ser muy peligroso.

El valor por defecto es {$S+} (sí se comprueba).
 

 $T:  Comprobación de punteros con tipo.

El operador @ devuelve la dirección de una variable (por ejemplo), por lo que se puede usar para crear variable que sean referencias a otras.

Si empleamos {$T-} (por defecto), @ siempre devolverá un puntero sin tipo, pero con {$T+} devolverá un puntero al tipo de la variable que se trate, por lo que sólo será compatible con punteros del mismo tipo.
 

 $V:  Comprobación de Strings.

Si se indica {$V-}, se podrá pasar como a una función o un procedimiento como parámetro un string de tamaño distinto al esperado, sin que el compilador proteste.

Con {$V+} (valor por defecto en TP7) no se puede.  Por ejemplo: si una función que hemos definido espera como parámetro un string[4] y le intentamos pasar una variable que hemos definido como string[2], el compilador nos dirá que son de distinto tipo.

En TP7 se recomienda usar la opción de "strings abiertos" (open strings), $P en vez de ésta.
 

 $X:  Sintaxis extendida.

Con sintaxis extendida ($X+, valor por defecto), se puede llamar a funciones como si fuesen procedimientos, despreciando los valores que devuelven.

Se puede ver un ejemplo en la lección sobre Turbo Vision, en el segundo apartado: MessageBox es realmente una función, que devuelve un código que nos informa sobre el botón que se ha pulsado; si no nos interesa este valor (como en el ejemplo, en el que sólo hay un botón), podemos llamar a la función como si fuese un procedimiento.
 


Las directivas parametrizadas son aquellas que incluyen una serie de parámetros, y que por ello son más flexibles que las anteriores, que sólo podian habilitar o desabilitar una función.
 

$I xxx:  Incluir fichero.
$L xxx:  Enlazar fichero objeto.
$M xxx:  Tamaños de memoria.
 
 

 $I xxx:  Incluir fichero.

Incluye otro fichero fuente en esa posición.  Si no se indica la extensión, se considerará que es .PAS.

Hoy en día, con la posibilidad de utilizar unidades, o incluso fuentes de gran tamaño (hasta 1 Mb en el editor TPX de TP7), no resulta necesaria.
 

 $L xxx:  Enlazar fichero objeto.

Incluye un fichero objeto (.OBJ, creado con ensamblador, por ejemplo) en esa posición.
 

 $M xxx:  Tamaños de memoria.

El formato es {$M Pila, HeapMin, HeapMax}

Por ejemplo, si queremos dejar 2K para la pila, y que el Heap (espacio de memoria para variables dinámicas) pueda estar entre 8 y 640K sería:

{$M 8192,8192,655360}



 

Las directivas condicionales son las que hacen que una parte del programa se compile sólo en ciertas circunstancias: si se ha especificado una cierta opción (directiva de tipo "switch") o si se ha definido una constante simbólica.

{$IFOPT}        Compila lo que sigue si se ha definido un cierto
                switch.  Por ejemplo:

                {$IFOPT N+}
                   var x: double;
                {$ELSE}
                   var x: real;
                {$ENDIF}

{$DEFINE Name}  Define una constante simbólica
{$UNDEF Name}   Cancela un definición
{$IFDEF Name}   Compila el código que sigue si el nombre se definió
{$IFNDEF}       Compila si no se definió
{$ELSE}         En caso contrario (ver ejemplo anterior)
{$ENDIF}        Fin de compilación condicional (ver ejemplo anterior)

Un ejemplo de su uso sería

{$DEFINE depurando}
[...]
{$IFDEF depurando}
    writeln('Ahora x vale', x );
{$ENDIF}

Una vez que el programa funcione correctamente, eliminamos la línea del $DEFINE (borrándola o con $UNDEF), y todas las partes que habíamos añadido para depurar entre $IFDEF y $ENDIF quedarán automáticamente sin compilar.
 

Actualizado el: 29-12-2011 11:44

AnteriorPosterior