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. La definición original de Pascal apareció en 1969 y un primer compilador en 1970. La primera versión de C apareció en 1972.
Ambos son descendientes de la serie lingüística ALGOL . ALGOL introdujo el soporte del lenguaje de programación para la programación estructurada , donde los programas se construyen con construcciones de entrada única y salida única como if , while , for y case . Pascal proviene directamente de ALGOL W , mientras que compartió algunas ideas nuevas con ALGOL 68 . El lenguaje C está más indirectamente relacionado con ALGOL, originalmente a través de B , BCPL y CPL , y luego a través de ALGOL 68 (por ejemplo, en el caso de struct
y union
) y también con Pascal (por ejemplo, en el caso de enumeraciones const
, typedef
y booleanos). Algunos dialectos de Pascal también incorporaron rasgos de C.
Los idiomas documentados aquí son el Pascal de Niklaus Wirth , tan estandarizado como ISO 7185 en 1982, y el C de Brian Kernighan y Dennis Ritchie , estandarizado en 1989. La razón es que estas versiones representan la versión madura del idioma, y también porque están relativamente cerca en el tiempo. Las características de ANSI C y C99 (los estándares C posteriores) y las características de implementaciones posteriores de Pascal ( Turbo Pascal , Free Pascal ) no se incluyen en la comparación, a pesar de las mejoras en robustez y funcionalidad que conferían.
Sintaxis
Sintácticamente , Pascal es mucho más ALGOL-como de C . Palabras clave en inglés se conservan en la que C utiliza símbolos de puntuación - Pascal tiene and
, or
y mod
donde los usos C &&
, ||
y %
por ejemplo. Sin embargo, C es 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 las declaraciones individuales dentro de una declaración compuesta; en lugar de C, terminan la declaración. En C, también son sintácticamente parte del enunciado (transformando una expresión en un enunciado). Esta diferencia se manifiesta principalmente en dos situaciones:
- en Pascal, un punto y coma nunca puede estar directamente antes
else
, mientras que en C, es obligatorio, a menos que se use una instrucción de bloque - la última instrucción antes de
end
ountil
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 */
. Esto solo es compatible con ciertos dialectos de Pascal como MIDletPascal.
En Pascal tradicional, hay y . Pascal moderno, como Object Pascal (Delphi, FPC), así como las implementaciones modernas de C permiten comentarios de estilo C ++{ 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.
Tanto C como Pascal usan palabras clave (palabras reservadas para el uso del idioma). 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, char sin signo ), mientras que en Pascal los nombres de tipos incorporados son identificadores normales predefinidos.
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
. En contraste, el fragmento de Pascal correspondiente var Y:^X;
es intrínsecamente inequívoco; el análisis correcto no requiere 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 int
es al menos 16 bits, long int
es no más corto que int
y short int
no más largo que int
.
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, el resultado de una operación se define para todos los tipos de enteros / subrangos, incluso si los resultados intermedios no encajan en un entero. El resultado es indefinido solo si no encaja en el entero / subrango en el lado izquierdo de la asignación. Esto puede implicar una restricción artificial en el rango de tipos de enteros, o puede requerir una ejecución lenta para manejar los resultados intermedios: Sin embargo, el compilador puede aprovechar subrangos restringidos para producir código más eficiente.
En C, los operandos primero deben promoverse al tamaño del resultado requerido: los resultados intermedios no están definidos si no encajan en el rango de los operandos promocionados. Si el rango del resultado requerido es mayor que el rango de operandos, esto normalmente produce un código ineficiente lento, incluso desde un buen compilador de optimización. Sin embargo, nunca se requiere ni se espera que un compilador de C maneje resultados intermedios fuera del rango: es responsabilidad del programador asegurarse de que todos los resultados intermedios encajen en el rango de operandos.
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) 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.
Tipos booleanos
En Pascal, boolean es un tipo enumerado. Los posibles valores de boolean son falsos y verdaderos . 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 : = i <> 0 ;
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.
Operaciones bit a bit
C permite 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 bit a bit directos (que también están disponibles en Pascal moderno). Ejemplo;
Pascal:
Estado : = Estado + [ StickyFlag ] ; Estado : = Estado - [ StickyFlag ] ; si ( StickyFlag en estado ) entonces ...(* Alternativamente, usando operadores bit a bit: *) Estado : = Estado o StickyFlag ; Estado : = Estado y no StickyFlag ; si StickyFlag y Status = StickyFlag 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.
Una nota sobre la implementación
Durante la evaluación de la expresión, y en ambos lenguajes , un valor booleano puede almacenarse internamente como un solo bit, un solo byte, una palabra de máquina completa, una posición en el código generado o como un código de condición en un registro de estado, dependiendo de la máquina. , compilador y situación; estos factores suelen ser más importantes que el lenguaje compilado.
Tipos de coma flotante
C tiene un modelo menos estricto de tipos de coma flotante que Pascal. En C, los enteros se pueden convertir implícitamente a números de punto flotante y viceversa (aunque la posible pérdida de precisión puede estar marcada por advertencias). En Pascal, los enteros se pueden convertir implícitamente a real
, pero la conversión de real
a integer
(donde se puede perder información) debe realizarse explícitamente a través de las funciones trunc()
y round()
, que truncan o redondean la fracción, respectivamente.
Tipos de enumeración
Tanto C como Pascal incluyen tipos de enumeración. Un ejemplo de Pascal:
tipo color = ( rojo , verde , azul ) ; var a : color ;
Ejemplo de CA:
enum color { rojo , verde , azul }; enum color a ;
Sin embargo, el comportamiento de los tipos en los dos idiomas es muy diferente. En C, se red
convierte en un sinónimo de 0, green
de 1, blue
de 2, y nada impide que se asigne a la variable un valor fuera de este rango a
. Además, operaciones como a = a + 1;
están estrictamente prohibidas en Pascal; en su lugar usarías a := succ(a);
. En C, las enumeraciones se pueden convertir libremente desde y hacia ints, pero en Pascal, la función ord () debe usarse para convertir de tipos enumerados a enteros, en la conversión opuesta debe usarse la operación encasillada como a := color(1)
para el green
retorno de valor.
Tipos estructurados
Tipos de matrices
Tanto C como Pascal permiten matrices de otros tipos complejos, incluidas otras matrices. Sin embargo, ahí termina la similitud entre los idiomas. Las matrices C se definen simplemente por un tipo base y el número de elementos:
int a [ TAMAÑO ];
y siempre están indexados desde 0 hasta SIZE − 1 (es decir, módulo SIZE).
En Pascal, el rango de índices a menudo se especifica mediante un subrango (como se introdujo anteriormente en los tipos simples). Los diez elementos de
var a : matriz [ 0 .. 9 ] de entero ;
estaría indexado por 0..9 (como en C en este caso). Sin embargo, los índices de matriz pueden ser cualquier tipo de datos ordinal , no solo rangos:
tipo TColor = ( rojo , verde , azul ) ; (* enumeración *) RGB = matriz [ TColor ] de 0 .. 255 ;var imagen : matriz [ 1 .. 640 , 1 .. 480 ] de RGB paleta var : matriz [ byte , 0 .. 2 ] de byte
Las cadenas que constan de n (> 1) caracteres se definen como matrices empaquetadas con rango 1..n.
Matrices y punteros
En las expresiones C, un identificador que representa una matriz se trata como un puntero constante al primer elemento de la matriz, por lo tanto, dadas las declaraciones int a[10]
y int *p;
la asignación p = a
es válida y hace que py a apunten a la misma matriz. Sin embargo, como el identificador a
representa una dirección constante , a = p
no es válido.
Si bien las matrices en C son fijas, los apuntadores a ellas son intercambiables. Esta flexibilidad permite a C manipular cualquier matriz de longitud utilizando el mismo código. También deja al programador con la responsabilidad de no escribir fuera de la matriz asignada, ya que no se incluyen comprobaciones en el lenguaje.
En Pascal, las matrices son un tipo distinto de los punteros. Esto hace posible la verificación de límites para matrices desde la perspectiva del compilador. Prácticamente todos los compiladores de Pascal admiten la verificación de rango como una opción de compilación . La capacidad de tener matrices que cambian de longitud en tiempo de ejecución y poder comprobarlas bajo el control del lenguaje se denomina a menudo "matrices dinámicas". En Pascal, el número de elementos en cada tipo de matriz se determina en tiempo de compilación y no se puede cambiar durante la ejecución del programa. Por tanto, no es posible definir una matriz cuya longitud dependa en modo alguno de los datos del programa.
C tiene la capacidad de inicializar matrices de longitud arbitraria. El sizeof
operador se puede utilizar para obtener el tamaño de una matriz inicializada estáticamente en código C. Por ejemplo, en el siguiente código, el índice de terminación del bucle se ajusta automáticamente si se cambia la lista de cadenas.
static char * wordlist [] = { "imprimir" , "salir" , "el" , "texto" , "mensaje" }; static int listSize = ( sizeof ( lista de palabras ) / sizeof ( lista de palabras [ 0 ])); int i ;para ( i = 0 ; i < listSize ; i ++ ) pone ( lista de palabras [ i ]); for ( i = listSize -1 ; i > = 0 ; i - ) pone ( wordlist [ i ]);
El Pascal original no tiene inicialización de matriz (fuera del caso de cadenas) ni un medio para determinar tamaños de matriz arbitrarios en tiempo de compilación.
Una forma de implementar el ejemplo anterior en Pascal, pero sin el ajuste automático de tamaño, es:
const minlist = 1 ; maxlist = 5 ; maxword = 7 ;escriba listrange = minlist .. maxlist ; rango de palabras = 1 .. palabra máxima ; palabra = contenido del registro : matriz empaquetada [ rango de palabras ] de char ; longitud : final del rango de palabras ; lista de palabras = matriz [ listrange ] de palabra ; var i : número entero ; palabras : lista de palabras ; procedimiento CreateList ( var w : lista de palabras ) ; comenzar w [ 1 ] . contenido : = 'imprimir' ; w [ 1 ] . longitud : = 5 ; w [ 2 ] . contenido : = 'fuera' ; w [ 2 ] . longitud : = 3 ; w [ 3 ] . contenido : = 'el' ; w [ 3 ] . longitud : = 3 ; w [ 4 ] . contenido : = 'texto' ; w [ 4 ] . longitud : = 4 ; w [ 5 ] . contenido : = 'mensaje' ; w [ 5 ] . longitud : = 7 ; terminar ;comenzar CreateList ( palabras ) ; para i : = minlist a maxlist hacer con palabras [ i ] hacer WriteLn ( contenido : longitud ) ; para i : = maxlist downto minlist hacer con palabras [ i ] do WriteLn ( contenido : longitud ) end .
Instrumentos de cuerda
En ambos idiomas, una cadena es una matriz primitiva de caracteres.
En Pascal, un literal de cadena de longitud n es compatible con el tipo packed array [1..n] of char
. En C, una cadena generalmente tiene el tipo char[n]
.
Pascal no admite matrices de longitud variable, por lo que cualquier conjunto de rutinas para realizar operaciones de cadena depende de un tamaño de cadena en particular. La extensión del "parámetro de matriz conforme" de Pascal ahora estandarizada resuelve esto en gran medida, y muchas o incluso la mayoría de las implementaciones de Pascal tienen soporte para cadenas nativas del lenguaje.
Los literales de cadena C terminan en nulo ; es decir, un carácter nulo final como un centinela de fin de cadena :
const char * p ; p = "la lluvia en España" ; / * terminado en nulo * /
La terminación nula debe mantenerse manualmente para las variables de cadena almacenadas en matrices (esto a menudo se maneja en parte por las rutinas de la biblioteca).
C carece de asignación de matriz o cadena incorporada, por lo que la cadena no se transfiere ap, sino que se hace que p apunte a la cadena constante en la memoria.
En Pascal, a diferencia de C, el primer elemento de carácter de la cadena está en el índice 1 y no en 0 (lo que lo lleva a tener un prefijo de longitud ). Esto se debe a que Pascal almacena la longitud de la cadena en el elemento 0 de la matriz de caracteres. Si esta diferencia no se comprende bien, puede provocar errores al transferir o intentar interconectar el código objeto generado por ambos lenguajes.
El desarrollador de FreeBSD, Poul-Henning Kamp , escribiendo en ACM Queue , se referiría más tarde a la victoria de las cadenas terminadas en nulo sobre las cadenas con prefijos de longitud como "el error de un byte más caro" de todos los tiempos. [1]
Tipos de registro
Tanto C como Pascal pueden declarar tipos de " registro ". En C, se denominan "estructuras".
estructura a { int b ; char c ; };
escriba a = registro b : entero ; c : char ; terminar ;
En Pascal, podemos usar la oración " con
escriba r = registro s : cadena ; c : char ; terminar ; var r1 : r ; comenzará con r1 hacer comenzar s : = 'foo' ; c : = 'b' ; terminar ;
No hay una característica equivalente a con en C.
En C, se puede especificar la longitud exacta de bits de un campo:
struct a { unsigned int b : 3 ; unsigned int c : 1 ; };
La cantidad de almacenamiento que se utiliza depende de los rasgos (por ejemplo, alineación de palabras) del sistema de destino.
Esta característica está disponible en Pascal usando la construcción de subrango (3 bits dan un rango de 0 a 7) en asociación con la palabra clave empaquetada :
escriba a = registro empaquetado b : 0 .. 7 ; c : 0 .. 1 ; terminar ;
Tanto C como Pascal admiten registros que pueden incluir diferentes campos superpuestos entre sí:
union a { int a ; flotar b ; };
escriba a = registro de mayúsculas y minúsculas booleano de falso : ( a : entero ) ; verdadero : ( b : real ) final ;
Ambos procesadores de idiomas tienen la libertad de asignar solo el espacio necesario para estos registros para contener el tipo más grande en la unión / registro.
La mayor diferencia entre C y Pascal es que Pascal admite el uso explícito de un "campo de etiqueta" para que el procesador de lenguaje determine si se está accediendo al componente válido del registro variante:
escriba a = caso de registro q : booleano de falso : ( a : entero ) ; verdadero : ( b : real ) final ;
En este caso, el campo de etiqueta q debe establecerse en el estado correcto para acceder a las partes adecuadas del registro.
Punteros
En C, los punteros pueden apuntar a la mayoría de las entidades del programa, incluidos objetos o funciones:
int a ; int * b ; int ( * comparar ) ( int c , int d ); int MyCompare ( int c , int d ); b = & a ; compare = & MyCompare ;
En C, dado que las matrices y los punteros tienen una equivalencia cercana, los siguientes son los mismos:
a = b [ 5 ]; a = * ( b + 5 ); a = * ( 5 + b ); a = 5 [ b ];
Por lo tanto, los punteros se utilizan a menudo en C como un método más para acceder a las matrices.
Para crear datos dinámicos, las funciones de la biblioteca malloc()
y free()
se utilizan para obtener y liberar bloques dinámicos de datos. Por tanto, la asignación de memoria dinámica no está integrada en el procesador de lenguaje. Esto es especialmente valioso cuando C se usa en kernels de sistemas operativos o destinos integrados, ya que estas cosas son muy específicas de la plataforma (no solo de la arquitectura) y requerirían cambiar el compilador de C para cada plataforma (o sistema operativo) en el que se usaría.
Pascal no tiene el mismo tipo de punteros que C, pero tiene un operador de indirección que cubre el uso más común de punteros C. Cada puntero está vinculado a un único elemento de datos dinámicos y solo se puede mover por asignación:
escriba a = ^ entero ; var b , c : a ; nuevo ( b ) ; c : = b ;
Los punteros en Pascal son seguros para escribir; es decir, un puntero a un tipo de datos solo se puede asignar a un puntero del mismo tipo de datos. Además, los punteros nunca se pueden asignar a variables que no sean punteros. La aritmética de punteros (una fuente común de errores de programación en C, especialmente cuando se combina con problemas de endianidad y tamaños de tipo independientes de la plataforma) no está permitida en Pascal. Todas estas restricciones reducen la posibilidad de errores relacionados con el puntero en Pascal en comparación con C, pero no evitan por completo las referencias de puntero no válidas en Pascal. Por ejemplo, se producirá un error de tiempo de ejecución si se hace referencia a un puntero antes de que se haya inicializado o después de que se haya eliminado.
Expresiones
Niveles de precedencia
Los lenguajes difieren significativamente en lo que respecta a la evaluación de expresiones, pero en general son comparables.
Pascal
- Negación lógica:
not
- Multiplicativo:
* / div mod and
- Aditivo:
+ - or
- Relacional:
= <> < > <= >= in
C
- Sufijo unario:
[] () . -> ++ --
- Prefijo unario:
& * + - ! ~ ++ -- (type) sizeof
- Multiplicativo:
* / %
- Aditivo:
+ -
- Cambiar:
<< >>
- Relacional:
< > <= >=
- Igualdad:
== !=
- Bit a bit y:
&
- Xor bit a bit:
^
- Bit a bit o:
|
- Lógico y:
&&
- Lógico o:
||
- Condicional:
? :
- Asignación:
= += -= *= /= %= <<= >>= &= ^= |=
- Operador de coma :
,
Mecanografía
La mayoría de los operadores sirven para varios propósitos en Pascal, por ejemplo, el signo menos puede usarse para negación, resta o diferencia de conjuntos (dependiendo tanto del tipo como del contexto sintáctico), el >=
operador puede usarse para comparar números, cadenas o conjuntos, y pronto. C usa símbolos de operador dedicados en mayor medida.
Pruebas de asignación e igualdad
Los dos idiomas utilizan diferentes operadores para la asignación. Pascal, como ALGOL , usa el operador de igualdad matemática =
para la prueba de igualdad y el símbolo :=
para la asignación, mientras que C, como B , usa el operador de igualdad matemática para la asignación. Por tanto, en C (y B) ==
se introdujo el nuevo símbolo para la prueba de igualdad.
Es un error común en C, ya sea por inexperiencia o por un simple error de escritura, colocar accidentalmente expresiones de asignación en declaraciones condicionales como if (a = 10) { ... }
. El código entre llaves siempre se ejecutará porque la expresión de asignación a = 10
tiene el valor 10, que no es cero y, por lo tanto, se considera "verdadero" en C; esto se debe en parte a que C (y ALGOL) permiten asignaciones múltiples en la forma a = b = c = 10;
que no es compatible con Pascal. También tenga en cuenta que a
ahora tiene el valor 10
, lo que puede afectar al siguiente código. Los compiladores de C recientes intentan detectar estos casos y advertir al usuario, solicitando una sintaxis menos ambigua como if ((a=10) != 0 ) { ... }
.
Este tipo de error no puede ocurrir en Pascal, ya que las asignaciones no son expresiones y no tienen un valor: usar el operador incorrecto causará un error de compilación inequívoco, y también es menos probable que alguien confunda el :=
símbolo con una prueba de igualdad.
Es de destacar que la expresión condicional de ALGOL en la forma a := if a > b then a else b;
tiene un equivalente en C pero no en Pascal.
Problemas de implementación
Cuando Niklaus Wirth diseñó Pascal, el deseo era limitar el número de niveles de precedencia (menos rutinas de análisis, después de todo). Por lo tanto, los operadores OR y OR exclusivo se tratan como un Addop y se procesan al nivel de una expresión matemática. De manera similar, el AND se trata como un Mulop y se procesa con Term. Los niveles de precedencia son
Operador de elemento de sintaxis de nivel 0 factor literal, variable 1 factor unario con signo menos, NO 2 término *, /, Y 3 expresión +, -, OR
Observe que solo hay UN conjunto de reglas de sintaxis, que se aplican a ambos tipos de operadores. De acuerdo con esta gramática, entonces, expresiones como
x + (y Y NO z) / 3
son perfectamente legales. Y, de hecho, lo son, en lo que respecta al analizador sintáctico. Pascal no permite la mezcla de variables aritméticas y booleanas, y cosas como esta se capturan en el nivel semántico, cuando llega el momento de generar código para ellas, en lugar de en el nivel de sintaxis.
Los autores de C adoptaron un enfoque diametralmente opuesto: tratan a los operadores como diferentes y, de hecho, en C no hay menos de 15 niveles. Eso es porque C también tiene los operadores '=', '+ =' y sus parientes, '<<', '>>', '++', '-', etc. Aunque en C los operadores aritméticos y booleanos son tratadas por separado, las variables no lo son: se puede realizar una prueba booleana sobre cualquier valor entero.
Conectivos lógicos
En Pascal, una expresión booleana que se basa en un orden de evaluación particular (posiblemente a través de efectos secundarios en las llamadas a funciones) se considera, más o menos, como un error. El compilador de Pascal tiene la libertad de usar cualquier orden que prefiera y siempre debe evaluar la expresión completa, incluso si el resultado se puede determinar mediante una evaluación parcial.
En C, la dependencia de boolean orden de evaluación es perfectamente legal, y, a menudo empleada sistemáticamente por el &&
y ||
los operadores junto con operadores como ++
, +=
, el operador de coma, etc. El &&
y ||
operadores así la función como combinaciones de operadores lógicos y condicionales declaraciones .
La evaluación de la expresión de cortocircuito se ha considerado comúnmente una ventaja para C debido al "problema de evaluación":
var i : número entero ; a : matriz empaquetada [ 1 .. 10 ] de char ; ... yo : = 1 ; mientras que ( i <= 10 ) y ( a [ i ] <> 'x' ) hago i : = i + 1 ; ...
Esta búsqueda aparentemente sencilla es problemática en Pascal porque el acceso a la matriz a [i] sería inválido para i igual a 11. Hay más de una forma de evitar este problema. El siguiente ejemplo presenta una variable booleana que indica si se ha encontrado o no el carácter de destino:
const strlen = 10 ; var i : número entero ; a : matriz empaquetada [ 1 .. strlen ] de char ; encontrado : booleano ; ... yo : = 1 ; encontrado : = falso ; mientras no se encuentra y ( i <= strlen ) hacer si ( a [ i ] = 'x' ) luego se encuentra : = verdadero else i : = i + 1 ; ...
Estructuras de Control
Las declaraciones para la construcción de estructuras de control son aproximadamente análogas y relativamente similares (al menos las tres primeras).
Pascal tiene:
- si cond entonces stmt else stmt
- mientras cond hacer stmt
- repetir stmt hasta cond
- para id : = expr a expr do stmt y para id : = expr downto expr do stmt
- caso expr de expr : stmt ; ... expr : stmt ; else: stmt ; final
C tiene:
- if ( cond ) stmt else stmt
- while ( cond ) stmt
- hacer stmt while ( cond );
- para ( expr ; cond ; expr ) stmt
- cambiar ( expr ) { caso expr : stmt ; ... caso expr : stmt ; predeterminado: stmt }
Pascal, en su forma original, no tenía un equivalente a default , pero una cláusula else equivalente es una extensión común. De lo contrario, los programadores de Pascal tenían que proteger las declaraciones de casos con una expresión como: si expr no está en [A..B], entonces por defecto .
C tiene las llamadas declaraciones de salida temprana que se interrumpen y continúan , y algunos Pascal también las tienen.
Tanto C como Pascal tienen una declaración goto . Sin embargo, dado que Pascal tiene procedimientos / funciones anidados, se pueden realizar saltos desde un procedimiento interno o función al que lo contiene; esto se usaba comúnmente para implementar la recuperación de errores. C tiene esta capacidad a través de ANSI C setjmp y longjmp . Esto es equivalente, pero posiblemente menos seguro, ya que almacena información específica del programa como direcciones de salto y marcos de pila en una estructura accesible al programador.
Funciones y procedimientos
Las rutinas de Pascal que devuelven un valor se denominan funciones; las rutinas que no devuelven un valor se denominan procedimientos. Todas las rutinas en C se denominan funciones; Las funciones de C que no devuelven un valor se declaran con un tipo de retorno de vacío .
Los procedimientos de Pascal se consideran equivalentes a las funciones "nulas" de C, y las funciones de Pascal son equivalentes a las funciones de C que devuelven un valor.
Las siguientes dos declaraciones en C:
int f ( int x , int y ); vacío k ( int q );
son equivalentes a las siguientes declaraciones en Pascal:
función f ( x , y : entero ) : entero ; procedimiento k ( q : entero ) ;
Pascal tiene dos tipos diferentes de parámetros: paso por valor y paso por referencia (VAR).
función f ( var k : integer ) : integer ; x : = f ( t ) ;
En C, todos los parámetros se pasan por valor, pero el paso por referencia se puede simular mediante punteros. El siguiente segmento es similar al segmento de Pascal anterior:
int f ( int * k ); // la función acepta un puntero como parámetro x = f ( & t );
C permite que las funciones acepten un número variable de parámetros, conocidos como funciones variadas .
int f ( int a , ...); f ( 1 , 2 , 3 , 4 , 5 );
La función f()
utiliza un conjunto especial de funciones que le permiten acceder a cada uno de los parámetros a su vez.
Además, Pascal tiene declaraciones de E / S integradas en el lenguaje para manejar una cantidad variable de parámetros, como Writeln
. Pascal permite anidar procedimientos y funciones . Esto es conveniente para permitir variables que son locales para un grupo de procedimientos, pero no globales. C carece de esta característica y la localización de variables o funciones solo se puede hacer para un módulo de compilación en el que las variables o funciones se habrían declarado estáticas .
C permite que las funciones se invoquen indirectamente a través de un puntero de función. En el siguiente ejemplo, la declaración (*cmpar)(s1, s2)
es equivalente a strcmp(s1, s2)
:
#include int ( * cmpar ) ( const char * a , const char * b ); const char * s1 = "hola" ; const char * s2 = "mundo" ;cmpar = & strcmp ; b = ( * cmpar ) ( s1 , s2 );
Pascal también permite pasar funciones y procedimientos como parámetros a funciones o procedimientos:
procedimiento ShowHex ( i : integer ) ; ... fin ;procedimiento ShowInt ( i : integer ) ; ... fin ;procedimiento de demostración ( procedimiento Mostrar ( i : número entero )) ; var j : número entero ; comenzar Mostrar ( j ) fin ;... Demostración ( ShowHex ) ; Demo ( ShowInt ) ; ...
Preprocesador
Early C no tenía declaraciones constantes ni declaraciones de tipo, y el lenguaje C se definió originalmente como que necesitaba un " preprocesador "; un programa separado, y pase, que manejaba las definiciones constantes, de inclusión y macro , para reducir el uso de la memoria. Más tarde, con ANSI C, obtuvo características de definiciones de tipos y constantes y el preprocesador también se convirtió en parte del lenguaje, lo que llevó a la sintaxis que vemos hoy.
La constante Pascal y las definiciones de tipo están integradas, pero había programadores que usaban un preprocesador también con Pascal (a veces el mismo que se usa con C), ciertamente no tan común como con C. Aunque a menudo se señala como una "falta" en Pascal, técnicamente C tampoco tiene la modularidad del programa ni las macros integradas. Tiene una facilidad de compilación separada simple de bajo nivel, sin embargo (tradicionalmente usando el mismo enlazador genérico usado para el lenguaje ensamblador), Pascal no lo tiene.
Tipo escapes
En C, el programador puede inspeccionar la representación a nivel de bytes de cualquier objeto apuntándolo con un char
puntero:
int a ; char * p = ( char * ) ( & a ); char c = * p ; // primer byte de un
Puede ser posible hacer algo similar en Pascal usando un registro variante no discriminado:
var a : entero ; b : real ; a2c : registro de caso booleano de falso : ( a : entero ) ; verdadero : ( b : real ) ; terminar ; terminar ; comenzar a2c . b : = b ; a : = a2c . a ; terminar ;
Aunque la conversión es posible en la mayoría de los compiladores e intérpretes de Pascal, incluso en el código anterior, ninguna estandarización de Pascal requiere que a2c.ay a2c.b compartan el mismo espacio de direcciones. Niklaus Wirth, el diseñador de Pascal, ha escrito sobre la naturaleza problemática de intentar escapes de tipos utilizando este enfoque:
"La mayoría de los implementadores de Pascal decidieron que esta verificación sería demasiado costosa, agrandaría el código y deterioraría la eficiencia del programa. Como consecuencia, el registro de variantes se convirtió en una característica favorita para violar el sistema de tipos de todos los programadores enamorados de los trucos, que generalmente se convierten en trampas y calamidades ".
Varios lenguajes ahora excluyen específicamente este tipo de escapes, por ejemplo, Java, C # y el propio Oberon de Wirth .
Archivos
En C, los archivos no existen como un tipo integrado (se definen en un encabezado del sistema) y todas las E / S se realizan a través de llamadas a la biblioteca. Pascal tiene el manejo de archivos integrado en el lenguaje.
Las declaraciones típicas que se utilizan para realizar E / S en cada idioma son:
printf ( "La suma es:% d \ n " , x );
Writeln ( 'La suma es:' , x ) ;
La principal diferencia es que C usa una "cadena de formato" que se interpreta para encontrar los argumentos de la función printf y convertirlos, mientras que Pascal lo realiza bajo el control del procesador de lenguaje. El método Pascal es posiblemente más rápido, porque no se realiza ninguna interpretación, pero el método C es altamente extensible.
Implementaciones y extensiones posteriores de Pascal
Algunas implementaciones populares de Pascal han incorporado virtualmente todas las construcciones de C en Pascal. Los ejemplos incluyen tipos de conversión, pudiendo obtener la dirección de cualquier variable, local o global, y diferentes tipos de enteros con propiedades especiales de promoción.
Sin embargo, la incorporación de la actitud indulgente de C hacia los tipos y las conversiones de tipos puede resultar en un Pascal que pierde parte o toda su seguridad de tipos. Por ejemplo, Java y C # se crearon en parte para abordar algunos de los problemas de seguridad de tipo percibidos de C, y tienen punteros "administrados" que no se pueden usar para crear referencias no válidas. En su forma original (como lo describe Niklaus Wirth ), Pascal califica como un lenguaje de puntero administrado, unos 30 años antes que Java o C #. Sin embargo, un Pascal combinado con C perdería esa protección por definición. En general, la menor dependencia de los indicadores para las tareas básicas lo hace más seguro que C en la práctica.
El estándar Extended Pascal extiende Pascal para admitir muchas cosas que C admite, que el estándar original Pascal no lo hizo, de una manera más segura. Por ejemplo, los tipos de esquema admiten (además de otros usos) matrices de longitud variable mientras mantienen la seguridad de tipo de llevar obligatoriamente la dimensión de la matriz con la matriz, lo que permite verificaciones automáticas en tiempo de ejecución para índices fuera de rango también para matrices de tamaño dinámico.
Ver también
- C , C ++
- Pascal , Object Pascal , Free Pascal , Delphi , Oxygene
Notas
- ^ Kamp, Poul-Henning (25 de julio de 2011), "El error de un byte más caro" , Cola de ACM , 9 (7), ISSN 1542-7730 , consultado el 2 de agosto de 2011
Otras lecturas
- Kathleen Jensen y Niklaus Wirth: PASCAL - Manual de usuario e informe. Springer-Verlag, 1974, 1985, 1991, ISBN 3-540-97649-3 [1]
- Brian Kernighan , Dennis Ritchie : El lenguaje de programación C . También llamado K&R, el libro original sobre C.
- Primero, Prentice Hall 1978; ISBN 0-13-110163-3 . Pre-ANSI C.
- 2º, Prentice Hall 1988; ISBN 0-13-110362-8 . ANSI C.
- Niklaus Wirth: Comente una nota sobre matrices dinámicas en PASCAL 37-38, ACM SIGPLAN Notices, Volumen 11, Número 1, enero de 1976.
- Niklaus Wirth: Recuerdos sobre el desarrollo de Pascal. 333-342, Avisos ACM SIGPLAN, Volumen 28, Número 3, marzo de 1993.
- ISO / IEC 9899 . El estándar oficial C: 1999, junto con informes de defectos y una justificación.
- Análisis detallado de la conversión de C a Pascal
- Alan R. Feuer, Narain H. Gehani: Comparación de los lenguajes de programación C y Pascal 73-92, Encuestas de computación de ACM, Volumen 14, Número 1, marzo de 1982.
- Comparación y evaluación de lenguajes de programación: Ada, C y Pascal , Ed. por Alan R. Feuer y Narain Gehani, Prentice Hall, 1984. ISBN 0-13-154840-9
- Scott Meyers: C ++ efectivo , 2a ed., Addison-Wesley, 1998, ISBN 0-201-92488-9
- Vincent Hayward: Anatomía comparada de los lenguajes de programación Pascal y C 50-60, ACM SIGPLAN Notices, Volumen 21, Número 5, mayo de 1986.
- Pascal para usuarios de C