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:
Dividiremos las directivas en tres tipos:
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.