Comparación de Pascal y C

format_list_bulleted Contenido keyboard_arrow_down
ImprimirCitar
Comparación de dos idiomas de programación

Los lenguajes de programación informática C y Pascal tienen tiempos de origen, influencias y propósitos similares. Ambos fueron utilizados para diseñar (y compilar) sus propios compiladores al principio de sus vidas. 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 de lenguajes ALGOL. ALGOL introdujo soporte de lenguaje de programación para programación estructurada, donde los programas se construyen a partir de construcciones de entrada y salida únicas como if, while, for y caso. Pascal surge directamente de ALGOL W, mientras que comparte algunas ideas nuevas con ALGOL 68. El lenguaje C está relacionado más indirectamente con ALGOL, originalmente a través de B, BCPL y CPL, y más tarde a través de ALGOL 68 (por ejemplo en el caso de struct y union) y también Pascal (por ejemplo en el caso de enumeraciones, const, typedef y booleanos). Algunos dialectos de Pascal también incorporaron rasgos de C.

Los lenguajes documentados aquí son el Pascal de Niklaus Wirth, estandarizado como ISO 7185 en 1982, y el C de Brian Kernighan y Dennis Ritchie, estandarizado en 1989. La razón es que ambas versiones representan la versión madura del lengua, y también porque están comparativamente cerca en el tiempo. Las características ANSI C y C99 (los últimos estándares C), 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 confirieron.

Sintaxis

Sintácticamente, Pascal es mucho más parecido a ALGOL que C. Las palabras clave en inglés se conservan cuando C usa símbolos de puntuación: Pascal tiene and, or y mod. donde C usa &&, || y %, por ejemplo. Sin embargo, C es más parecido a ALGOL que Pascal con respecto a las declaraciones (simples), conservando la sintaxis type-name variable-name. Por ejemplo, C puede aceptar declaraciones al inicio de cualquier bloque, no sólo el bloque externo de una función.

Uso de punto y coma

Otra diferencia, más sutil, es el papel del punto y coma. En Pascal, el punto y coma separa declaraciones individuales dentro de una declaración compuesta; en cambio, en C, terminan la declaración. En C, también son sintácticamente parte de la declaración (transformando una expresión en una declaración). Esta diferencia se manifiesta principalmente en dos situaciones:

  • en Pascal, un ymicolón nunca puede ser directamente antes else, mientras que en C, es obligatorio, a menos que se utilice una declaración de bloque
  • la última declaración antes de una end o until no está obligado a ser seguido por un ymicolon

Se puede colocar un punto y coma superfluo en la última línea antes de fin, insertando así formalmente una declaración vacía.

Comentarios

En C tradicional, solo hay /* comentarios de bloque */. Esto sólo es compatible con ciertos dialectos de Pascal como MIDletPascal.

En Pascal tradicional, hay { comentarios de bloque } y (* comentarios de bloque *). Pascal moderno, como Object Pascal (Delphi, FPC), así como las implementaciones modernas de C, permiten comentarios de estilo C++ // comentarios de línea

Identificadores y palabras clave

C y Pascal difieren en su interpretación de mayúsculas y minúsculas. C distingue entre mayúsculas y minúsculas, mientras que Pascal no, 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 se utilizan a menudo para diferenciar identificadores de sistemas especiales en C.

Tanto C como Pascal utilizan palabras clave (palabras reservadas para el uso del lenguaje). Algunos ejemplos son if, while, const, for y goto, que son palabras clave. que resulta ser común a ambos idiomas. En C, los nombres de tipos básicos integrados también son palabras clave (p. ej., int, char) o combinaciones de palabras clave (p. ej., unsigned char). ), mientras que en Pascal los nombres de tipos integrados son identificadores normales predefinidos.

Definiciones, declaraciones y bloques

En Pascal, las definiciones de subrutinas comienzan con las palabras clave procedimiento (no se devuelve ningún valor) o función (se devuelve un valor) y escriben definiciones con tipo. En C, todas las subrutinas tienen definiciones de función (los procedimientos son funciones nulas) y las definiciones de tipo utilizan la palabra clave typedef. Ambos idiomas utilizan una combinación de palabras clave y puntuación para definiciones de tipos complejos; por ejemplo, las matrices se definen mediante la palabra clave array en Pascal y mediante puntuación en C, mientras que las enumeraciones se definen mediante la palabra clave enum en C pero mediante puntuación en Pascal.

En las subrutinas Pascal, begin y end delimitan un bloque de declaraciones precedidas por declaraciones locales, mientras que las funciones C usan "{" y "}" para delimitar un bloque de declaraciones opcionalmente precedidas 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 bloques, lo cual es una forma de rodea esto. Por su sintaxis del cuerpo de una subrutina, Pascal exige que las declaraciones ocurran antes que las declaraciones. Pascal también permite encapsular definiciones de tipos y funciones (no sólo declaraciones de variables) mediante definiciones de funciones a cualquier nivel de profundidad.

Implementación

Las gramáticas de ambos idiomas son de 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 tipos, mientras que en Pascal solo existe una construcción de este tipo, la asignación. Por ejemplo, el fragmento de C X * Y; podría ser una declaración de Y como un objeto cuyo tipo es un puntero a X, o un expresión-sentencia que multiplica X y Y. Por el contrario, los fragmentos de Pascal correspondientes var Y: ^X; y Z:= X * Y; son inherentemente inequívocos; 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, se puede omitir un nombre de tipo en la mayoría de los contextos y el tipo predeterminado int (que corresponde a integer en Pascal) se asume implícitamente (sin embargo, tales valores predeterminados son se consideran malas prácticas en C y a menudo se señalan con advertencias).

C admite diferentes tamaños y modos con y sin signo para números enteros mediante el uso de modificadores como long, short, signed, unsigned. , etc. El significado exacto del tipo entero resultante depende de la máquina, lo que puede garantizar es que long int no sea 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, lo que garantiza que char sea un solo byte y int sea al menos dos bytes.

Subrangos

En Pascal, se realiza un fin similar declarando un subrango de número entero (un compilador puede optar por asignar una cantidad menor de almacenamiento para la variable declarada):

Tipo a = 1..100; b = -20..20; c = 0..100000;

Esta característica 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 caben en un número entero. El resultado no está definido sólo si no cabe en el número entero/subrango del 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 los subrangos restringidos para producir código más eficiente.

En C, los operandos deben ser promovidos primero al tamaño del resultado requerido: los resultados intermedios son indefinidos si no encajan en el rango de los operados promovidos. 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 optimizador. Sin embargo, un compilador C nunca se requiere o se espera que pueda manejar resultados intermedios fuera de rango: es responsabilidad de los programadores asegurar que todos los resultados intermedios se ajusten al rango de operandos.

Implementaciones preestándar de C, así como de Small-C et al. permitió que los tipos de enteros y punteros se mezclaran con relativa libertad.

Tipos de caracteres

En C, el tipo de carácter es char, que es un tipo de número entero que no mide más que short int. Por lo tanto, expresiones como 'x'+1 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

no firmado char Uc = 255; * límite común */firmado char sc = -128; * límite negativo común */

Si el tipo char debe considerarse como signed o unsigned de forma predeterminada depende de la implementación.

En Pascal, los caracteres y los números enteros son tipos distintos. Las funciones incorporadas del compilador ord() y chr() se pueden utilizar para encasillar caracteres individuales al valor entero correspondiente del conjunto de caracteres en uso, y viceversa. p.ej. en sistemas que utilizan el juego de caracteres ASCII ord('1') = 49 y chr(9) es un carácter TAB.

Tipos booleanos

En Pascal, booleano es un tipo enumerado. Los valores posibles de booleano son falso y verdadero, con valor ordinal de falso = 0 y verdadero = 1. Para conversión a entero , ord se utiliza:

i := ord()b);

No existe una función estándar para entero a booleano, sin embargo, la conversión es sencilla en la práctica:

b := i  0;

C no tiene ningún tipo booleano. C utiliza operadores relacionales con valores binarios (<, >, ==, !=, <=, >=) que pueden considerarse booleanos en el sentido de que siempre dan resultados que son ya sea cero o uno. Como todas las pruebas (&&, ||, ?:, if, mientras, etc.) se realizan mediante comprobaciones cero, falso está representado por cero, mientras que verdadero está representado por cualquier otro valor. Esto es visible en el tipo de datos numérico bool definido en stdbool.h.

Operaciones bit a bit

C permite el uso de 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 datos bit a bit, los conjuntos. Los conjuntos permiten al programador establecer, borrar, intersectar 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:

Situación := Situación + [StickyFlag];Situación := Situación - [StickyFlag];si ()StickyFlag dentro Situación) entonces ...(*) Alternativamente, usando operadores de bitwise: *)Situación := Situación o StickyFlag;Situación := Situación y no StickyFlag;si StickyFlag y Situación = StickyFlag entonces ...

C:

Situación  StickyFlag;Situación " ~StickyFlag;si ()Situación " StickyFlag) {} ...

Aunque las operaciones de bits en números 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 números enteros y conjuntos.

Una nota sobre la implementación

Durante la evaluación de expresiones, y en ambos idiomas, un valor booleano se puede almacenar 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, según la máquina, el compilador y la situación; Estos factores suelen ser más importantes que el lenguaje compilado.

Tipos de punto flotante

C tiene un modelo de tipos de coma flotante menos estricto que Pascal. En C, los números enteros se pueden convertir implícitamente en números de coma flotante, y viceversa (aunque las advertencias pueden indicar una posible pérdida de precisión). En Pascal, los números 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 mediante 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 C:

enum color {}rojo, verde, azul};enum color a;

Sin embargo, el comportamiento de los tipos en los dos idiomas es muy diferente. En Pascal, las enumeraciones son ordinales y se analizan utilizando las funciones ord(), succ() y pred() y son distintas de la matriz . Estructura . En C, las enumeraciones se implementan de hecho como arrays y red se convierte en solo un sinónimo de 0, green de 1, blue para 2, y nada impide que se asigne un valor fuera de este rango a la variable a. Además, operaciones como a = a + 1; están estrictamente prohibidas en Pascal; en su lugar, usaría a:= succ(a);. En C, las enumeraciones se pueden convertir libremente hacia y desde ints, pero en Pascal, la función ord() debe usarse para convertir de tipos enumerados a enteros, en la conversión opuesta se debe usar una operación encasillada como a:= color(1) para el valor devuelto green.

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 TAMAÑO-1 (es decir, módulo TAMAÑO).

En Pascal, el rango de índices se especifica a menudo por un subrango (como se introduce bajo tipos simples arriba). Los diez elementos de

Var a : array[0..9] de entero;

se indexaría entre 0 y 9 (como en C en este caso). Los índices de matriz pueden ser cualquier tipo de datos ordinales, sin embargo, no solo rangos:

Tipo TColor = ()rojo, verde, azul); (* enumeración*) RGB = array[TColor] de 0..255;Var Imagen : array[1..640, 1..480] de RGBVar paleta : array[byte, 0..2] de byte

Las cadenas que constan de n (>1) caracteres se definen como matrices empaquetadas con rango 1...n.

Arrays y punteros

En 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 p y 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 punteros a ellas son intercambiables. Esta flexibilidad permite a C manipular una matriz de cualquier longitud usando el mismo código. También deja al programador con la responsabilidad de no escribir fuera de la matriz asignada, ya que no hay comprobaciones integradas 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 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 verificarlas bajo el control del lenguaje a menudo se denomina "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 lo tanto, no es posible definir una matriz cuya longitud dependa de alguna manera de los datos del programa. (Nota: desde 1986 y Turbo Pascal 3, que era el estándar de la industria, GetMem() permite matrices dinámicas en Pascal cotidiano, si no en el estándar ISO)

C tiene la capacidad de inicializar matrices de longitud arbitraria. El operador sizeof 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.

estática char *wordlist[] = {} "print", "Fuera", "el", "texto", "Mensaje" };estática int listSize = ()tamaño()wordlist)/tamaño()wordlist[0])int i;para ()i=0; ic)listSize; i++) puts()wordlist[i]);para ()i=listSize-1; i>=0; i--) puts()wordlist[i]);

Del mismo modo Pascal moderno, p.e. Delphi y Free Pascal, tiene una habilidad similar. Las matrices inicializadas se pueden implementar como:

Var wordlist: array de cuerda = [ 'print' ', 'out ', 'the ', 'texto ', "Mensaje" ']; i: Integer;Comienzo para i := Baja()wordlist) a Alto()wordlist) do Writeln()wordlist[i]); para i := Alto()wordlist) abajo Baja()wordlist) do Writeln()wordlist[i]);final.

Pascal original no tiene inicialización de matrices (fuera del caso de cadenas) ni medios para determinar tamaños de matrices arbitrarios en tiempo de compilación. Una forma de implementar el ejemplo anterior en Pascal original, pero sin el ajuste de tamaño automático, es:

const minlist = 1; maxlist = 5; Maxword = 7;Tipo listrange = minlist .. maxlist; Wordrange = 1..Maxword; palabra = récord contenidos: empaquetado array [Wordrange] de char; longitud: Wordrange final; wordlist = array[listrange] de palabra;Var i: entero; palabras: wordlist;procedimiento CreateList()Var w: wordlist);Comienzo w[1].contenidos := 'print'; w[1].longitud := 5; w[2].contenidos := 'out '; w[2].longitud := 3; w[3].contenidos := 'el '; w[3].longitud := 3; w[4].contenidos := 'texto'; w[4].longitud := 4; w[5].contenidos := "Mensaje" '; w[5].longitud := 7;final;Comienzo CreateList()palabras); para i := minlist a maxlist do con palabras[i] do WriteLn()contenidos: longitud); para i := maxlist abajo minlist do con palabras[i] do WriteLn()contenidos: longitud)final.

Cuerdas

En ambos idiomas, una cadena es una matriz primitiva de caracteres.

En Pascal, una cadena literal de longitud n es compatible con el tipo matriz empaquetada [1..n] de 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 cadenas depende de un tamaño de cadena en particular. El ahora estandarizado Pascal "parámetro de matriz conforme" La extensión 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 centinela de fin de cadena:

const char *p;p = "la lluvia en España"; /* null-terminated */

La terminación nula debe mantenerse manualmente para las variables de cadena almacenadas en matrices (esto a menudo se maneja en parte mediante rutinas de biblioteca).

C carece de asignación de cadena o matriz incorporada, por lo que la cadena no se transfiere a p, 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 hace que tenga 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 migrar 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 prefijo de longitud como "el error de un byte más caro". #34; alguna vez.

Tipos de registros

Tanto C como Pascal pueden declarar "registro" tipos. En C, se denominan "estructuras".

struct a {} int b; char c;};
Tipo a = récord b: entero; c: char;final;

En Pascal, podemos usar la oración "with name_of_record do" para utilizar directamente los campos de ese registro, como variables locales, en lugar de escribir nombre_del_registro.nombre_del_campo. Aquí hay un ejemplo:

Tipo r = récord s: cuerda; c: char; final;Varr1 : r;Comienzocon r1 do Comienzo s := 'foo '; c := 'b ';final;

No existe ninguna característica equivalente a with en C.

En C, se puede especificar la longitud exacta en bits de un campo:

struct a {} no firmado int b:3; no firmado int c:1;};

La cantidad de almacenamiento que se utiliza depende de las características (por ejemplo, alineación de palabras) del sistema de destino.

Esta característica está disponible en Pascal mediante el uso de la construcción de subrango (3 bits dan un rango de 0 a 7) en asociación con la palabra clave empaquetado:

Tipo a = empaquetado récord b: 0..7; c: 0..1;final;

Tanto C como Pascal admiten registros que pueden incluir diferentes campos superpuestos entre sí:

sindicato a {} int a; flotador b;};
Tipo a = récord Caso boolean de falso: ()a: entero); verdadero: ()b: real)final;

Ambos procesadores de lenguaje son libres de asignar solo el espacio necesario para estos registros para contener el tipo más grande en la unión/registro. En Pascal, estas construcciones se denominan registros variantes, que no deben confundirse con el tipo de datos Variante definido en Free Pascal.

La mayor diferencia entre C y Pascal es que Pascal admite el uso explícito de un "campo de etiquetas" para que el procesador de lenguaje determine si se está accediendo al componente válido del registro de variante:

Tipo a = récord Caso q: boolean de falso: ()a: entero); verdadero: ()b: real)final;

En este caso, el campo de etiqueta q debe configurarse en el estado correcto para acceder a las partes adecuadas del registro.

Consejos

En C, se pueden hacer que los punteros apunten 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;comparar = "MyCompare;

En C, dado que las matrices y los punteros tienen una estrecha equivalencia, lo siguiente es lo mismo:

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 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 núcleos de sistemas operativos o en 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 tiene el mismo tipo de punteros que C, a través del operador de referencia ^ en lugar del * de C. Cada puntero está vinculado a un único elemento de datos dinámico, y sólo se puede mover por asignación:

Tipo 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 sólo 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 letra independientes de la plataforma) no está permitida en Pascal. Todas estas restricciones reducen la posibilidad de errores relacionados con punteros en Pascal en comparación con C, pero no evitan por completo las referencias de punteros 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

  1. Negación lógica: not
  2. Multiplicativo: * / div mod and
  3. Aditivo: + - or
  4. Relación: = = in

C

  1. Unry postfix: [] (). -> ++ --
  2. Unry prefijo: & * + - ! ~ ++ -- (type) sizeof
  3. Multiplicativo: * / %
  4. Aditivo: + -
  5. Cambio: <>
  6. Relación: =
  7. Igualdad: == !=
  8. Bitwise y: &
  9. Bitwise xor: ^
  10. Bitwise o: |
  11. Logical and: &&
  12. Lógica o: ||
  13. Condición: ?:
  14. Asignación: = += -= *= /= %= <>= &= ^= |=
  15. Comma operator: ,

Escribiendo

La mayoría de los operadores sirven para varios propósitos en Pascal, por ejemplo, el signo menos se puede usar para negación, resta o diferencia de conjuntos (dependiendo del tipo y del contexto sintáctico), el operador >= se puede utilizar para comparar números, cadenas o conjuntos, etc. C utiliza 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, al igual que B, usa el operador de igualdad matemática para la asignación. . En C (y B) se eligió el símbolo == de FORTRAN para la prueba de igualdad.

Es un error común en C, ya sea por inexperiencia o por un simple error tipográfico, poner 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" 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 el siguiente código. Los compiladores de C recientes intentan detectar estos casos y advierten 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 provocará un error de compilación inequívoco y también es menos probable que alguien confunda el := símbolo para una prueba de igualdad.

Es notable que la expresión condicional de ALGOL en la forma z:= if a > b entonces a else b; tiene un equivalente en C (el operador ternario de CPL) pero no en Pascal, que usará si a > b entonces z:=a; más z:=b;.

Problemas de implementación

Cuando Niklaus Wirth diseñó Pascal, el deseo era limitar el número de niveles de precedencia (después de todo, menos rutinas de análisis). 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 Sintaxis de Nivel

0 factor literal, variable
1 factor firmado unry minus, NO
2 term *, /, AND
3 expresiones +, -, OR

Observe que solo hay UN conjunto de reglas de sintaxis que se aplican a ambos tipos de operadores. Según esta gramática, entonces, expresiones como

 x + (y AND NOT z) / 3

son perfectamente legales. Y, de hecho, lo son, en lo que respecta al analizador. Pascal no permite la mezcla de variables aritméticas y booleanas, y cosas como esta quedan atrapadas 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 hay nada menos que 15 niveles. Esto se debe a que C también tiene los operadores '=', '+=' y sus parientes, '<<', '>>', '++', '--&#39 ;, etc. Aunque en C los operadores aritméticos y booleanos se tratan por separado, las variables no: 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 llamadas a funciones) se considera, más o menos, como un error. El compilador de Pascal tiene la libertad de utilizar cualquier orden que prefiera y siempre debe evaluar la expresión completa incluso si el resultado puede determinarse mediante una evaluación parcial. (Nota: desde Turbo Pascal 3 (1986) la evaluación booleana de cortocircuito está disponible en Pascal cotidiano, si no en el estándar ISO).

En C, la dependencia del orden de evaluación booleano es perfectamente legal y, a menudo, se emplea sistemáticamente utilizando los operadores && y ||. junto con operadores como ++, +=, el operador de coma, etc. El && y || funcionan así como combinaciones de operadores lógicos y declaraciones condicionales.

La evaluación de expresiones de cortocircuito se ha considerado comúnmente una ventaja para C debido al "problema de evaluación":

Var i: entero; a: empaquetado array [1..10] de char;  ... i := 1; mientras ()i . 10) y ()a[i]  'x ') do i := i+1; ...

Esta búsqueda aparentemente sencilla es problemática en Pascal porque el acceso a la matriz a[i] no sería válido para i igual a 11. Hay más de una forma de evitar este problema. El siguiente ejemplo introduce una variable booleana que indica si se ha encontrado o no el carácter de destino:

const strlen = 10;Var i: entero; a: empaquetado array [1..strlen] de char; encontrado: boolean;  ... i := 1; encontrado := falso; mientras no encontrado y ()i . strlen) do si ()a[i] = 'x ') entonces encontrado := verdadero más i := i+1; ...

Alternativamente, la prueba para el final de la matriz se puede separar del acceso a la matriz y una declaración goto puede salir de la búsqueda si se encuentra el objetivo:

etiqueta 99;const strlen = 10;Var i: entero; a: empaquetado array [1..strlen] de char;  ... i := 1; repetición si a[i] = 'x ' entonces Goto 99; i := i+1 hasta i  strlen; 99:  ...

Estructuras de control

Las declaraciones para las estructuras de control de edificios son más o menos análogas y relativamente similares (al menos las tres primeras).

Pascal tiene:

  • si cond entonces stmt más stmt
  • mientras cond do stmt
  • repetición stmt hasta cond
  • para id:= expreso a expreso do stmt y para id:= expreso abajo expreso do stmt
  • Caso expreso de expreso : stmt; expreso : stmt; más: stmt; final

C tiene:

  • si ()cond) stmt más stmt
  • mientras ()cond) stmt
  • do stmt mientras ()cond);
  • para ()expreso; cond; expreso) stmt
  • interruptor ()expresoCaso expreso : stmt; Caso expreso : stmt; default: 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: if expr not en [A..B] entonces caso-predeterminado.

C tiene las llamadas sentencias de salida temprana break y continue, y algunos Pascal también las tienen.

Tanto C como Pascal tienen una instrucción goto. Sin embargo, dado que Pascal tiene procedimientos/funciones anidados, se pueden realizar saltos desde un procedimiento o función interno 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 para el programador.

Funciones y procedimientos

Las rutinas Pascal que devuelven un valor se llaman funciones; Las rutinas que no devuelven un valor se denominan procedimientos. Todas las rutinas en C se llaman funciones; Las funciones C que no devuelven un valor se declaran con un tipo de retorno de void.

Los procedimientos Pascal se consideran equivalentes a C "void" funciones, y las funciones Pascal son equivalentes a funciones C que devuelven un valor.

Las dos declaraciones siguientes en C:

int f()int x, int Sí.);vacío k()int q);

son equivalentes a las siguientes declaraciones en Pascal:

función f()x, Sí.: entero): entero;procedimiento k()q: entero);

Pascal tiene dos tipos diferentes de parámetros: paso por valor y paso por referencia (VAR). En ambos casos, se utiliza el nombre de la variable al llamar (no es necesario el operador de dirección).

función f()z: entero; Var k: entero): entero; // función acepta dos números enteros, uno por valor, uno por referenciaComienzo z:=1; // variable externa u no será modificada, pero el valor local se modifica en el alcance de la función k:=1; // la variable externa t se modificará porque fue aprobada por referencia // hasta aquí, z existe e igual a 1Final;x := f()u,t); // las variables u y t se pasan a la llamada: el valor de u y la referencia a t

En C, todos los parámetros se pasan por valor, pero el paso por referencia se puede simular utilizando punteros. El siguiente segmento es similar al segmento de Pascal anterior:

int f()int z, int *k) {} //función acepta un int (por valor) y un puntero a int (también por valor) como parámetro z=1; // idem Pascal, el valor local se modifica pero la u externa no será modificada *k=1; // variable referenciado por k (por ejemplo, t) será modificado // hasta aquí, z existe e igual a 1}x = f()u,"t); // el valor de u y la dirección (valor de) de variable t se pasan a la llamada

Una de las diferencias más importantes entre C y Pascal es la forma en que manejan los parámetros en la pila durante una llamada de subrutina: Esto se llama convención de llamada: Los parámetros de estilo PASCAL se colocan en la pila en orden de izquierda a derecha. La convención de llamada STDCALL de C coloca los parámetros en la pila en orden de derecha a izquierda.

La llamada a procedimiento estilo Pascal se realiza con:

  • caller empujando parámetros en la pila en orden izquierdo a derecha (opposite de __cdecl)
  • llamando a la función
  • la pila está limpiada por la callee
; ejemplo de llamada de estilo pascal.
; NOTA: __stdcall empujaría los argumentos en orden inverso.
empujar arg1
push arg2
empujar arg3
función de llamada
; ninguna limpieza de pila a la vuelta: Callee lo hizo

La ventaja de la llamada PASCAL sobre STDCALL es que el código es ligeramente más pequeño, aunque el impacto del tamaño sólo es visible en programas grandes, y la recursividad funciona más rápido.

Es casi imposible ejecutar correctamente las funciones variables con los métodos PASCAL y STDCALL, porque sólo quien llama sabe realmente cuántos argumentos se pasaron para limpiarlos.

C permite que las funciones acepten un número variable de parámetros, conocidas como funciones variadas, utilizando un mecanismo torpe de va_list ap;, va_start(ap, count); , va_arg(ap, type); con disponibilidad limitada de type (ejemplo: nada para bool)

int f()int a, ...);f()1, 2, 3, 4, 5);

La función f() utiliza un conjunto especial de funciones (varargs) que le permiten acceder a cada uno de los parámetros por turno.

Pascal y C también tienen algunas funciones de E/S variadas, por ejemplo WriteLn() y printf().

Los Pascal modernos permiten un número variable de parámetros para funciones: procedimiento writeLines (argumentos constantes: matriz de constantes); // analizado vía: para argumento en argumentos hacer También permiten interactuar con funciones de varargs C: Función PrintF1(fmt: pchar); cdec; varargs; externo 'c' nombre 'printf';

Pascal permite anidar procedimientos y funciones. Esto es conveniente para permitir variables que sean locales para un grupo de procedimientos, pero no globales. C carece de esta característica y la localización de variables o funciones sólo se puede realizar para un módulo de compilación en el que las variables o funciones se habrían declarado estáticas.

C y Pascal permiten 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 Identificando.hint ()*cmpar)const char *a, const char *b);const char *s1 = "hola";const char *s2 = "mundo";cmpar = "strcmp;b = ()*cmpar)s1, s2);

En Pascal, las funciones y procedimientos se pueden pasar como parámetros a funciones o procedimientos:

procedimiento ShowHex()i: entero);...final;procedimiento ShowInt()i: entero);...final;procedimiento Demo()procedimiento Show()i: entero);Var j: entero;Comienzo Show()j)final;... Demo()ShowHex); Demo()ShowInt);...

Preprocesador

Los primeros C no tenían declaraciones constantes ni declaraciones de tipo, y el lenguaje C se definió originalmente como que necesitaba un "preprocesador"; un programa separado, y pasar, que manejaba definiciones constantes, de inclusión y de macros, para mantener bajo el uso de memoria. Posteriormente, con ANSI C, obtuvo características de definiciones de tipos y constantes y el preprocesador también pasó a formar parte del lenguaje, dando lugar a la sintaxis que vemos hoy.

Las definiciones de tipos y constantes de Pascal están integradas y no necesitan un preprocesador. Había programadores que usaban un preprocesador también con Pascal (a veces el mismo usado con C), ciertamente no tan común como con C. Aunque a menudo se señala como una "carencia" En Pascal, técnicamente C no tiene modularidad de programa ni macros integradas. Tiene una sencilla función de compilación separada de bajo nivel; sin embargo (tradicionalmente usa el mismo enlazador genérico usado para el lenguaje ensamblador), Pascal no la tiene.

Escriba escapes

En C, el programador puede inspeccionar la representación a nivel de bytes de cualquier objeto apuntándole con un puntero char:

int a;char *p = ()char *)"a);char c = *p; // primer byte of a

Puede ser posible hacer algo similar en Pascal usando un registro de variantes indiscriminado:

Var a: entero; b: real; a2c: récord Caso boolean de falso: ()a: entero); verdadero: ()b: real); final; final;Comienzo a2c.b := b; a := a2c.a;final;

Aunque el casting es posible en la mayoría de los compiladores e intérpretes Pascal, incluso en el código anterior a2c.a y a2c.b no son requeridos por ninguna estandarización Pascal para compartir el mismo espacio de direcciones. Niklaus Wirth, diseñador de Pascal, ha escrito sobre la naturaleza problemática de intentar escapar tipo utilizando este enfoque:

"La mayoría de los implementadores de Pascal decidieron que esta comprobación sería demasiado costosa, ampliando código y deteriorando la eficiencia del programa. Como consecuencia, el registro de la variante se convirtió en una característica favorita para romper el sistema de tipo por todos los programadores enamorados de trucos, que generalmente se convierten en trampas y calamidades".

Varios lenguajes ahora excluyen específicamente estos tipos de escape, 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 manejo de archivos integrado en el lenguaje.

Las declaraciones típicas utilizadas para realizar E/S en cada idioma son:

printf()"La suma es: %dn", x);
Writeln()' La suma es: ', x);

La principal diferencia es que C utiliza una "cadena de formato" eso 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. Podría decirse que el método Pascal es 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 prácticamente todas las construcciones de C en Pascal. Los ejemplos incluyen conversiones de tipos, pudiendo obtener la dirección de cualquier variable, local o global, y diferentes tipos de números enteros con propiedades de promoción especiales.

Sin embargo, la incorporación de la actitud indulgente de C hacia los tipos y las conversiones de tipos puede dar como resultado que Pascal pierda 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 tipos percibidos en C, y han "gestionado" punteros que no se pueden utilizar para crear referencias no válidas. En su forma original (como la describe Niklaus Wirth), Pascal califica como un lenguaje de puntero administrado, unos 30 años antes que Java o C#. Sin embargo, un Pascal fusionado con C perdería esa protección por definición. En general, la menor dependencia de punteros para tareas básicas lo hace más seguro que C en la práctica.

El estándar Pascal extendido extiende Pascal para admitir muchas cosas que C admite, que el estándar original Pascal no admitía, 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.

Contenido relacionado

Spl (Unix)

spl es el nombre de una colección de rutinas o macros del kernel de Unix utilizadas. para cambiar el nivel de prioridad de interrupción. Históricamente...

PERLA (lenguaje de programación)

PEARL, o lenguaje en tiempo real de automatización de procesos y experimentos, es un lenguaje de programación diseñado para realizar múltiples tareas y...

Tabla de métodos virtuales

En programación informática, una tabla de métodos virtuales una tabla de funciones virtuales, una tabla de llamadas virtuales , tabla de despacho, vtable o...

Datosflex

DataFlex es un lenguaje de programación de alto nivel orientado a objetos y una herramienta visual de cuarta generación para desarrollar aplicaciones de...

Hacer bucle while

En muchos lenguajes de programación de computadoras, un bucle do while es una declaración de flujo de control que ejecuta un bloque de código y luego...
Más resultados...
Tamaño del texto:
undoredo
format_boldformat_italicformat_underlinedstrikethrough_ssuperscriptsubscriptlink
save