Los lenguajes de programación de computadoras C y Pascal tienen tiempos de origen, influencias y propósitos similares. Ambos se utilizaron para diseñar (y compilar) sus propios compiladores al principio de su vida útil.
Tanto C como Pascal son lenguajes de programación antiguos: la definición original de Pascal apareció en 1969 y un primer compilador en 1970. La primera versión de C apareció en 1972. Si bien C no cambió mucho con el tiempo, Pascal ha evolucionado mucho y hoy en día el La gran mayoría de la programación de Pascal se realiza en Object Pascal moderno , no en el antiguo Pascal procedimental. El antiguo Pascal procedimental actual se limita esencialmente a la programación de microcontroladores con herramientas como mikroPascal , mientras que Object Pascal es el dialecto principal y se utiliza con herramientas como Delphi , Lazarus (IDE) y Free Pascal .
Lo que se documenta aquí es el Object Pascal moderno utilizado en Free Pascal y Delphi. La C documentada es C99, estandarizada en 1999.
Sintaxis
Sintácticamente, Object Pascal es mucho más Algol-como que las palabras clave C. Inglés se retienen donde C utiliza símbolos de puntuación - Pascal tiene and
, or
y mod
donde los usos C &&
, ||
y %
por ejemplo. Sin embargo, C es en realidad más parecido a Algol que Pascal con respecto a declaraciones (simples), conservando la sintaxis de nombre de variable de nombre de tipo . Por ejemplo, C puede aceptar declaraciones al comienzo de cualquier bloque, no solo el bloque exterior de una función.
Uso de punto y coma
Otra diferencia, más sutil, es el papel del punto y coma . En Pascal, los puntos y comas separan los enunciados individuales dentro de un enunciado compuesto, mientras que terminan el enunciado en C. También son sintácticamente parte del enunciado en sí en C (transformando una expresión en un enunciado). Esta diferencia se manifiesta principalmente en dos situaciones:
- nunca puede haber un punto y coma directamente antes
else
en Pascal mientras que es obligatorio en C (a menos que se use una declaración de bloque) - la última instrucción antes de una
end
no debe ir seguida de un punto y coma
Se puede poner un punto y coma superfluo en la última línea antes del final , insertando formalmente una declaración vacía .
Comentarios
En C tradicional, solo hay /* block comments */
. Desde C99, también hay //Line comments
. En Object Pascal, hay , y .{ block comments }
(* block comments *)
// Line comments
Identificadores y palabras clave
C y Pascal difieren en su interpretación de mayúsculas y minúsculas. C es sensible a mayúsculas, mientras que Pascal no es, por lo tanto MyLabel
y mylabel
son nombres distintos en C pero idénticos en Pascal. En ambos idiomas, los identificadores constan de letras y dígitos, con la regla de que el primer carácter no puede ser un dígito. En C, el guión bajo cuenta como una letra, por lo que incluso _abc es un nombre válido. Los nombres con un guión bajo inicial se utilizan a menudo para diferenciar identificadores de sistema especiales en C. Pascal también acepta el carácter _ como parte de los identificadores, sin diferencia con C.
Tanto C como Pascal usan palabras clave (palabras reservadas para el uso del lenguaje en sí). Algunos ejemplos son if , while , const , for y goto , que son palabras clave que resultan ser comunes a ambos idiomas. En C, los nombres de tipos incorporados básicos también son palabras clave (por ejemplo , int , char ) o combinaciones de palabras clave (por ejemplo, unsigned char ), mientras que en Pascal los nombres de tipos incorporados son identificadores normales predefinidos.
Sin embargo, los compiladores de Object Pascal recientes permiten escapar de palabras clave con &, esta característica es principalmente necesaria cuando se comunica directamente con sistemas OOP externos como COM y COCOA que pueden usar campos y métodos basados en palabras clave de Pascal. C no tiene forma de escapar de las palabras clave.
Definiciones, declaraciones y bloques
En Pascal, las definiciones de procedimiento comienzan con las palabras clave procedimiento o función y las definiciones de tipo con tipo . En C, las definiciones de función están determinadas por el contexto sintáctico, mientras que las definiciones de tipo usan la palabra clave typedef
. Ambos idiomas utilizan una combinación de palabras clave y puntuación para las definiciones de tipos complejos; por ejemplo, las matrices se definen mediante la matriz de palabras clave en Pascal y la puntuación en C, mientras que las enumeraciones se definen mediante la palabra clave enum
en C pero mediante la puntuación en Pascal.
En las funciones de Pascal, begin y end delimitan un bloque de declaraciones (propiamente dicho), mientras que las funciones de C usan "{" y "}" para delimitar un bloque de declaraciones precedidas opcionalmente por declaraciones. C (antes de C99) define estrictamente que cualquier declaración debe ocurrir antes de las declaraciones dentro de un bloque en particular, pero permite que los bloques aparezcan dentro de los bloques, que es una forma de evitar esto. Pascal es estricto en cuanto a que las declaraciones deben ocurrir antes que las declaraciones, pero permite que las definiciones de tipos y funciones, no solo declaraciones de variables, sean encapsuladas por definiciones de funciones a cualquier nivel de profundidad.
Implementación
Las gramáticas de ambos idiomas son de un tamaño similar. Desde una perspectiva de implementación, la principal diferencia entre los dos lenguajes es que para analizar C es necesario tener acceso a una tabla de símbolos para los tipos, mientras que en Pascal solo hay una construcción de este tipo, la asignación. Por ejemplo, el fragmento C X * Y;
podría ser una declaración de Y
ser un objeto cuyo tipo es un puntero a X
, o una expresión-declaración que multiplica X
y Y
. El fragmento de Pascal correspondiente var Y:^X;
es inequívoco sin una tabla de símbolos.
Tipos simples
Enteros
Pascal requiere que todas las declaraciones de variables y funciones especifiquen su tipo explícitamente. En C tradicional, un nombre de tipo puede omitirse en la mayoría de los contextos y el tipo predeterminado int
(que corresponde a integer
Pascal) se asume implícitamente (sin embargo, tales valores predeterminados se consideran una mala práctica en C y a menudo se señalan con advertencias).
C alojar diferentes tamaños y con y sin signo modos para números enteros mediante el uso de modificadores tales como long
, short
, signed
, unsigned
, etc. El significado exacto del tipo entero resultante es dependiente de la máquina, sin embargo, lo que puede ser garantizado es que long int
no es más corto que int
y int
no es más corto que short int
. Sin embargo, en el estándar C, se especifican al menos tamaños mínimos de tipos que garantizan char
que sean de un solo byte y int
de al menos dos bytes.
Subrangos
En Pascal, se realiza un final similar al declarar un subrango de entero (un compilador puede elegir asignar una cantidad menor de almacenamiento para la variable declarada):
escriba a = 1 .. 100 ; b = - 20 .. 20 ; c = 0 .. 100000 ;
Esta función de subrango no es compatible con C.
Una diferencia importante, aunque sutil, entre C y Pascal es cómo promueven las operaciones con números enteros. En Pascal, todas las operaciones en enteros o subintervalos de enteros tienen el mismo efecto, como si todos los operandos fueran promovidos a un entero completo. En C, hay reglas definidas sobre cómo promover diferentes tipos de enteros, típicamente con el tipo resultante de una operación entre dos enteros que tiene una precisión mayor o igual que las precisiones de los operandos. Esto puede hacer que el código de máquina generado a partir de C sea eficiente en muchos procesadores. Un compilador Pascal altamente optimizado puede reducir, pero no eliminar, este efecto bajo las reglas estándar de Pascal.
La (única) implementación previa al estándar de C, así como Small-C et al. permitió que los tipos de punteros y enteros se entremezclaran con relativa libertad.
Tipos de caracteres
En C es el tipo de carácter char
que es una especie de número entero que es no más short int
,. Expresiones como, 'x'+1
por tanto, son perfectamente legales, al igual que declaraciones como int i='i';
y char c=74;
.
Esta naturaleza entera de char
(un byte de ocho bits en la mayoría de las máquinas) se ilustra claramente mediante declaraciones como
carácter sin firmar uc = 255 ; / * * Límite común / firmado carbón sc = -128 ; / * límite negativo común * /
Si el char
tipo debe considerarse signed
o unsigned
por defecto depende de la implementación.
En Pascal, los caracteres y los números enteros son tipos distintos. El compilador incorporado funciona ord()
y chr()
se puede usar para encasillar caracteres individuales al valor entero correspondiente del juego de caracteres en uso, y viceversa. por ejemplo, en sistemas que utilizan el juego de caracteres ASCII ord('1') = 49
y chr(9)
es un carácter TAB.
Además del Char
tipo, Object Pascal también tiene WideChar
que representar caracteres Unicode. En C, esto generalmente se implementa como una macro o typedef
con un nombre wchar_t
, que es simplemente un alias de int
.
Tipos booleanos
En Pascal, boolean es un tipo enumerado. Los posibles valores de boolean son falsos y verdaderos , con un valor ordinal de falso = 0 y verdadero = 1, los demás valores no están definidos. Para la conversión a entero , se usa ord :
i : = ord ( b ) ;
No existe una función estándar para el número entero en booleano , sin embargo, la conversión es simple en la práctica:
b : = booleano ( i ) ; // Generará errores de verificación de rango adecuados para valores no definidos con las verificaciones de rango activadas.
C tiene operadores relacionales con valores binarios (<,>, ==,! =, <=,> =) Que pueden considerarse booleanos en el sentido de que siempre dan resultados que son cero o uno. Como todas las pruebas (&&, ||,?:, Si , mientras , etc.) se realizan mediante comprobaciones de cero, falso se representa con cero, mientras que verdadero se representa con cualquier otro valor.
Para interactuar con COM, Object Pascal ha añadido ByteBool
, WordBool
y el LongBool
tipo cuyo tamaño aspectos de su prefijo y que siga la tabla C verdad.
Free Pascal ha agregado los tipos booleanos de Pascal adecuados con el sufijo de tamaño ( boolean8, 16, 32, 64
) para interactuar con GLIB, que usa gboolean
un tipo booleano de 32 bits con la tabla de verdad de Pascal.
Operaciones bit a bit
El programador de C a veces puede usar operadores bit a bit para realizar operaciones booleanas. Se debe tener cuidado porque la semántica es diferente cuando los operandos utilizan más de un bit para representar un valor.
Pascal tiene otro método más abstracto y de alto nivel para tratar con conjuntos de datos bit a bit . Los conjuntos permiten al programador establecer, borrar, intersecar y unir valores de datos bit a bit, en lugar de utilizar operadores directos a bit. Ejemplo;
Pascal:
Estado : = Estado + [ StickyFlag ] ; // o Incluir (Estado, StickyFlag); Estado : = Estado - [ StickyFlag ] ; // o Excluir (Estado, StickyFlag); si ( StickyFlag en estado ) entonces ...
C:
Estado | = StickyFlag ; Estado & = ~ StickyFlag ; if ( Status & StickyFlag ) { ...
Aunque las operaciones de bits en enteros y las operaciones en conjuntos pueden considerarse similares si los conjuntos se implementan utilizando bits, no existe un paralelo directo entre sus usos a menos que sea posible una conversión no estándar entre enteros y conjuntos.
Pascal también podría hacer operaciones bit a bit de la misma manera como C mediante el uso de and
, or
, not
y xor
los operadores. Estos operadores normalmente funcionan con valores booleanos, pero cuando los operandos son números enteros, se comportan como operadores bit a bit. Esto es posible porque booleano y entero son tipos incompatibles distintos. Por lo tanto, el código C anterior podría escribirse en Pascal como:
Estado : = Estado o StickyFlag ; Estado : = Estado y no StickyFlag ; si Status y StickyFlag <> 0 entonces ...
Tipos avanzados
Tipo de cadena
En C, la cadena permanece como puntero al primer elemento de una matriz de caracteres terminada en nulo , como lo era en 1972. Aún se tiene que usar el soporte de biblioteca de
para manipular cadenas.
Object Pascal tiene muchos tipos de cadenas porque cuando se introduce un nuevo tipo, el antiguo se mantiene por compatibilidad con versiones anteriores. Esto sucedió dos veces, una vez con Delphi 2 (introducción de ansistring) y Delphi 2009 (Unicodestring). Además de los tipos de cadenas principales (short-, ansi, wide-, unicodestring) y los tipos de caracteres correspondientes (ansichar, widechar = unicodechar), todos los tipos derivados del tipo de carácter también tienen algunas propiedades de cadena (puntero a char, matriz de char , matriz dinámica de char, puntero a matriz de char, etc.).
En Object Pascal, string
es un tipo administrado por el compilador y se cuenta por referencias (si es necesario), es decir, su administración de almacenamiento es manejada por el compilador (o más exactamente, por el código de tiempo de ejecución insertado por el compilador en el ejecutable) . La concatenación de cadenas se realiza con el +
operador, y la comparación de cadenas se puede hacer con los operadores relacionales estándar (mayúsculas y minúsculas): < <= = <> >= >
.
Object Pascal también proporciona cadenas compatibles con C bajo el tipo PAnsiChar
, con rutinas de manipulación definidas en la Strings
unidad. Además, Object Pascal proporciona una amplia variedad de tipos de cadenas:
ShortString
, que internamente es uncon N como el número máximo de caracteres que se pueden almacenar y el índice 0 que contiene la longitud de la cadena. Se pueden almacenar un máximo de 255 caracteres en amatriz [ 0 .. N ] de Char ;
ShortString
, porque el límite superior de un byte sin signo es 255 y la matriz de contenedor está definida para tener un máximo de 255 caracteres de datos (recuerde que el índice 0 contiene la longitud de la cadena). N se da en la definición de tipo o en la declaración de variable (ver ejemplo a continuación)AnsiString
, una versión dinámica de longitud ilimitada y contada por referencias deShortString
. Desde Delphi 2009, tiene un campo que señala la codificación de los contenidos.WideString
, en Windows (win32 / 64 / ce) compatible con COM BSTR, UCS2 / UTF16 refcontado por COM. En sistemas distintos de Windows, igual a Unicodestring.UnicodeString
, comoWideString
, pero codificado en UTF-16
Por conveniencia, String
se proporciona el tipo simple , que, según el modificador del compilador, podría significar ShortString
, AnsiString
o UnicodeString
. Una convención adicional utilizada es que si se da un límite al número de caracteres, es un ShortString
, de lo contrario es el otro.
Short-
y las Ansi-
cadenas se pueden entremezclar libremente al manipular cadenas; el compilador realizará una conversión silenciosa cuando sea necesario. Tenga en cuenta que si el tipo de cadena de destino es ShortString
, es posible que se produzca un truncamiento silencioso debido a la longitud máxima permitida.
Ejemplo:
tipo TString80 = String [ 80 ] ; var ss : ShortString ; s80 : Cadena [ 80 ] ; // declara una cadena (corta) de longitud máxima 80 s80t : TString80 ; // igual que arriba astr : AnsiString ; s : Cadena ; // podría significar String [255], AnsiString o UnicodeString begin ss : = astr + s80 ; // SÍ, esto es posible y la conversión se realiza de forma transparente por el final del compilador ;
Tipo de matriz
Matriz estática
En C, no existe un concepto real de matriz; solo hay una pseudoconstrucción para declarar el almacenamiento de múltiples variables del mismo tipo. Las matrices en C no conocen su propia longitud y se hace referencia a ellas mediante un puntero al primer elemento, por lo que siempre se basan en 0. Ejemplo:
// declara int "matriz" nombrada a de longitud 10 int a [ 10 ]; // imprime el primer elemento, o más precisamente el elemento en la dirección retenida por a + 0 printf ( "% d" , a [ 0 ]); // imprime el segundo elemento, o más precisamente el elemento en la dirección retenida por a + 1 printf ( "% d" , a [ 1 ]); // pasar una matriz a una función, o más precisamente pasar el puntero al primer elemento alguna función ( a ); // igual que arriba alguna función ( & a [ 0 ]);
Para obtener la longitud de la matriz, hay que calcular . Por lo tanto, para contar la longitud de una matriz de enteros, utilice: . Es un error común calcular esto en una función que espera una matriz como argumento. A pesar de su apariencia, una función solo puede aceptar un puntero como argumento, no la matriz real. Por lo tanto, dentro de la función, la matriz se trata como un puntero simple. Ejemplo:sizeof(
sizeof(intarr) / sizeof(int)
// Esta función NO acepta una matriz, sino un puntero a int // Semánticamente, es lo mismo que: int * a void func ( int a []) { // ¡INCORRECTO! Devolvería sizeof (puntero) / sizeof (int) int len = sizeof ( a ) / sizeof ( int ); }int main () { int a [ 5 ]; // correcto, devolvería 5 int len = sizeof ( a ) / sizeof ( int ); func ( a ); return 0 ; }
Una solución común al problema anterior es pasar siempre la longitud de la matriz como un argumento de función, y las funciones que esperan un argumento de matriz también deben proporcionar un marcador de posición para su longitud.
A pesar de su tratamiento como puntero, no todas las construcciones de estilo de puntero se pueden usar para hacer arreglos. Por ejemplo, este código se compilaría bien pero causaría una infracción de acceso cuando se ejecute:
void func ( int * a ) { // ¡ERROR DE TIEMPO DE EJECUCIÓN! a se asigna estáticamente a = ( int * ) malloc ( sizeof ( int ) * 10 ); }int main () { int a [ 5 ]; func ( a ); }
Se debe tener cuidado al diseñar dicho código, y la documentación debe indicarlo explícitamente para evitar que los usuarios cometan tal error.
No se permite la asignación entre matrices estáticas y se debe usar la memcpy
función y sus variantes para copiar datos entre matrices.
En Pascal, una matriz se declara usando la array
palabra clave, especificando su límite inferior y superior, y su tipo base. Este último generalmente se define como un tipo de rango. Por ejemplo:
escriba T10IntegerArray = matriz [ 1 .. 10 ] de Integer ; TNegativeLowerBoundArray = matriz [ - 5 .. 5 ] de Entero ; TNamedIndexTypeArray = array [ Low ( Char ) .. High ( Char )] of Integer ; var IntegerArray : T10IntegerArray ; NegArray : TNegativeLowerBoundArray ; NamedIndexTypeArray : TNamedIndexTypeArray ;
Las matrices conocen sus límites superior e inferior (e implícitamente su longitud), y los límites se pasan cuando una función espera una matriz como argumento. Las funciones Low()
, High()
y Length()
recuperar el límite inferior, límite superior y longitud de la matriz, respectivamente, en cualquier contexto.
Sin una conversión explícita, las matrices no se pueden convertir en punteros y es un error de tiempo de compilación. Ésta es una propiedad de la programación con seguridad de tipos.
Se permite la asignación entre matrices estáticas. La asignación copia todos los elementos de la matriz de origen al destino. Es obligatorio que los límites superior e inferior sean compatibles entre el origen y el destino. Si de alguna manera son diferentes, entonces se pueden usar Move
para copiar parcialmente los datos. Sin embargo, dado que Move
es una función de bajo nivel, se debe usar con cuidado. Es responsabilidad del programador asegurarse de que el movimiento de datos no exceda los límites de destino ni de origen. Ejemplo:
escriba TArray1 = matriz [ 1 .. 10 ] de Integer ; TArray2 = matriz [ 1 .. 5 ] de Entero ; var a , b : TArray1 ; c : TArray2 ; comenzar a : = b ; // OK // Copia todos los elementos de c a a, sobrescribiendo elementos desde el 1er índice hasta el 1er índice + Longitud (c) Move ( c , a , Length ( c ) * SizeOf ( Integer )) ; // Copia todos los elementos de c a a, comenzando en el índice 5 de Move ( c , a [ 5 ] , Length ( c ) * SizeOf ( Integer )) ; // Copia los primeros 5 elementos de b a c Move ( b , c , 5 * SizeOf ( Integer )) ; final .
Matriz dinámica
C no tiene soporte de lenguaje para declarar y usar matrices dinámicas . Sin embargo, debido a su sintaxis de desreferencia de puntero, una matriz dinámica podría implementarse con funciones de administración de memoria, generalmente las de
. Ejemplo:
int tamaño = 10 ; int * un = ( int * ) malloc ( sizeof ( int ) * tamaño ); // asigna una matriz dinámica de entero con tamaño 10 int i ;for ( i = 0 ; i < size ; i ++ ) ... // haz algo con una [i]tamaño * = 2 ; int * temp = realloc ( a , sizeof ( int ) * size ); // duplica el espacio, conservando los elementos existentes if ( temp == NULL ) error ( "¡No hay suficiente memoria!" ); a = temp ; ... // haz algo con un free ( a ); // liberar el almacenamiento
Como se puede ver, nuevamente la longitud no se mantiene automáticamente, y la reasignación debe usar una variable adicional para proteger contra errores de memoria insuficientes. La asignación entre matrices dinámicas sigue la regla de asignación de punteros.
Object Pascal proporciona soporte a nivel de lenguaje para matrices dinámicas. Se declara omitiendo los límites superior e inferior. Entonces uno debe llamar a la SetLength()
función para asignar el almacenamiento. Las matrices dinámicas en Object Pascal se cuentan por referencias, por lo que uno no tiene que preocuparse por liberar el almacenamiento. Las matrices dinámicas siempre están basadas en cero. Las tres funciones Low()
, High()
y Length()
todavía recuperarían el límite inferior, el límite superior y la longitud de la matriz correctamente. Ejemplo:
tipo TIntArray = matriz de Integer ; T2DimIntArray = matriz de matriz de Integer ; var a : TIntArray ; a2 : T2DimIntArray ; i , j : entero ; comenzar SetLength ( a , 10 ) ; // asigna 10 almacenamiento para i : = Low ( a ) to High ( a ) do ... // haz algo con un [i] SetLength ( a2 , 10 , 10 ) ; // asignar 10 x 10 de almacenamiento para i : = Bajo ( a2 ) a Alto ( a2 ) hacer para j : = Bajo ( a2 [ i ]) a Alto ( a2 [ i ]) hacer ... // hacer algo con un [i, j] final ;
La asignación entre matrices dinámicas copia la referencia de la matriz de origen al destino. Si se requiere una copia real, se puede usar la Copy
función. Ejemplo:
tipo TIntegerArray = matriz de Integer ; var a , b : TIntegerArray ; comenzar ... // inicializar ayb a : = b ; // a ahora apunta a la misma matriz apuntada por b a [ 1 ] : = 0 ; // b [1] también debería ser 0 después de esto a : = Copy ( b , 3 , 5 ) ; // Copiar 5 elementos de b comenzando desde el índice 3 // a accedería de 0 a 4 pero al final .
Otras lecturas
- Free Pascal: Referencia del lenguaje [1]