Jump to content

Сравнение Паскаля и C

Языки программирования C и Pascal имеют схожее время возникновения, влияние и цели. Оба они использовались для разработки (и компиляции) своих собственных компиляторов в самом начале своей жизни. Исходное определение Паскаля появилось в 1969 году, а первый компилятор — в 1970 году. Первая версия C появилась в 1972 году.

Оба являются потомками языковой серии АЛГОЛ . АЛГОЛ представил язык программирования, поддерживающий структурное программирование , где программы состоят из конструкций с одним входом и одним выходом, таких как if , while , for и case . Паскаль произошел непосредственно от АЛГОЛА W , но в нем были общие некоторые новые идеи с АЛГОЛом 68 . Язык C более косвенно связан с АЛГОЛом, первоначально через B , BCPL и CPL , а затем через АЛГОЛ 68 (например, в случае struct и union), а также Паскаль (например, в случае перечислений, const, typedef и логические значения). Некоторые диалекты Паскаля также включали черты C.

Документированные здесь языки — это Паскаль Никлауса Вирта , стандартизированный как ISO 7185 в 1982 году, и C Брайана Кернигана и Денниса Ритчи , стандартизированный в 1989 году. Причина в том, что обе эти версии представляют собой зрелую версию языка, и еще и потому, что они сравнительно близки по времени. Функции ANSI C и C99 (более поздние стандарты C), а также функции более поздних реализаций Pascal ( Turbo Pascal , Free Pascal ) не включены в сравнение, несмотря на улучшения в надежности и функциональности, которые они обеспечили.

Синтаксис

[ редактировать ]

Синтаксически Паскаль гораздо больше похож на АЛГОЛ, C. чем Английские ключевые слова сохраняются там, где C использует знаки препинания – Паскаль and, or, и mod где C использует &&, ||, и % например. Однако C более похож на АЛГОЛ, чем на Паскаль, в отношении (простых) объявлений, сохраняя синтаксис имени типа и имени переменной . Например, C может принимать объявления в начале любого блока, а не только внешнего блока функции.

Использование точки с запятой

[ редактировать ]

Другое, более тонкое отличие — роль точки с запятой . В Паскале точка с запятой разделяет отдельные операторы внутри составного оператора; вместо этого в C они завершают оператор. В C они также синтаксически являются частью оператора (преобразуя выражение в оператор). Эта разница проявляется в основном в двух ситуациях:

  • в Паскале точка с запятой никогда не может стоять непосредственно перед else, тогда как в C это обязательно, если не используется оператор блока
  • последнее утверждение перед end или until не обязательно после точки с запятой

Лишнюю точку с запятой можно поставить в последней строке перед end , тем самым формально вставив пустой оператор .

Комментарии

[ редактировать ]

В традиционном C есть только /* block comments */. Это поддерживается только некоторыми диалектами Паскаля, такими как MIDletPascal.

В традиционном Паскале есть { block comments } и (* block comments *). Современный Паскаль, такой как Object Pascal (Delphi, FPC), а также современные реализации C допускают комментарии в стиле C++. // line comments

Идентификаторы и ключевые слова

[ редактировать ]

Си и Паскаль различаются в интерпретации верхнего и нижнего регистра. C чувствителен к регистру, а Паскаль — нет, поэтому MyLabel и mylabel это разные имена в C, но идентичные в Pascal. В обоих языках идентификаторы состоят из букв и цифр, с тем правилом, что первый символ не может быть цифрой. В C подчеркивание считается буквой, поэтому даже _abc является допустимым именем. Имена с подчеркиванием в начале часто используются для различения специальных системных идентификаторов в C.

И C, и Pascal используют ключевые слова (слова, зарезервированные для использования в языке). Примеры: if , while , const , for и goto — ключевые слова, общие для обоих языков. В C основные имена встроенных типов также являются ключевыми словами (например, int , char ) или комбинациями ключевых слов (например, unsigned char ), тогда как в Паскале имена встроенных типов являются предопределенными обычными идентификаторами.

Определения, объявления и блоки

[ редактировать ]

В Паскале определения подпрограмм начинаются с ключевых слов процедура (значение не возвращается) или функция (значение возвращается), а определения типов — с type . В C все подпрограммы имеют function определения (процедуры void functions) и определения типов используют ключевое слово typedef. Оба языка используют сочетание ключевых слов и знаков препинания для определений сложных типов; например, массивы определяются ключевым словом array в Паскале и знаками препинания в C, а перечисления определяются ключевым словом enum на C, но с пунктуацией на Паскале.

В подпрограммах Pascal начало и конец разделяют блок операторов, которым предшествуют локальные объявления, в то время как функции C используют «{» и «}» для разделения блока операторов, которым необязательно предшествуют объявления: C (до C99) строго определяет, что любые объявления должны происходят перед операторами внутри конкретного блока, но позволяют блокам появляться внутри блоков, что является способом обойти эту проблему. Благодаря синтаксису тела подпрограммы Паскаль обеспечивает, чтобы объявления выполнялись перед операторами. Паскаль также позволяет определения инкапсулировать типов и функций, а не только объявления переменных, в определения функций на любой уровень глубины.

Выполнение

[ редактировать ]

Грамматики обоих языков имеют одинаковый размер. С точки зрения реализации основное различие между этими двумя языками состоит в том, что для анализа C необходимо иметь доступ к таблице символов для типов, тогда как в Паскале есть только одна такая конструкция — присваивание. Например, фрагмент C X * Y; может быть декларацией Y быть объектом, тип которого является указателем на Xили выражение-оператор, которое умножает X и Y. Напротив, соответствующие фрагменты Паскаля var Y : ^X; и Z := X * Y; по своей сути однозначны; правильный синтаксический анализ не требует таблицы символов.

Простые типы

[ редактировать ]

Целые числа

[ редактировать ]

Паскаль требует, чтобы во всех объявлениях переменных и функций явно указывался их тип. В традиционном C имя типа может быть опущено в большинстве контекстов, а тип по умолчанию int (что соответствует integer в Паскале) тогда неявно предполагается (однако такие значения по умолчанию считаются плохой практикой в ​​C и часто помечаются предупреждениями).

C поддерживает разные размеры, а также знаковые и беззнаковые режимы для целых чисел с помощью модификаторов, таких как long, short, signed, unsignedи т. д. Точное значение результирующего целочисленного типа зависит от машины, поэтому можно гарантировать, что long int не короче, чем int и int не короче, чем short int. Однако в стандарте C указаны как минимум минимальные размеры типов, что гарантирует char быть одним байтом и int быть не менее двух байт.

Поддиапазоны

[ редактировать ]

В Паскале аналогичное завершение выполняется путем объявления поддиапазона целого числа (компилятор может затем выделить меньший объем памяти для объявленной переменной):

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

Эта функция поддиапазона не поддерживается C.

Основное, хотя и незначительное, различие между C и Pascal заключается в том, как они реализуют целочисленные операции. В Паскале результат операции определяется для всех типов целых чисел и поддиапазонов, даже если промежуточные результаты не умещаются в целое число. Результат не определен, только если он не вписывается в целое число или поддиапазон в левой части присваивания. Это может подразумевать искусственное ограничение диапазона целочисленных типов или может требовать медленного выполнения для обработки промежуточных результатов. Однако компилятор может воспользоваться преимуществами ограниченных поддиапазонов для создания более эффективного кода.

В C операнды сначала должны быть повышены до размера требуемого результата: промежуточные результаты не определены, если они не вписываются в диапазон повышенных операндов. Если диапазон требуемого результата превышает диапазон операндов, это обычно приводит к медленному неэффективному коду, даже при использовании хорошего оптимизирующего компилятора. Однако от компилятора C никогда не требуется и не ожидается, что он будет обрабатывать промежуточные результаты, выходящие за пределы диапазона: ответственность за то, чтобы все промежуточные результаты вписывались в диапазон операндов, лежит на программисте.

Предварительные реализации C, а также Small-C и др. целочисленные типы и типы указателей разрешено относительно свободно смешивать .

Типы персонажей

[ редактировать ]

В C тип символа char это своего рода целое число, длина которого не превышает short int, . Такие выражения, как 'x'+1 поэтому совершенно законны, как и такие заявления, как int i='i'; и char c=74;.

Эта целочисленная природа char (один байт) наглядно иллюстрируется такими объявлениями, как

unsigned char uc = 255;  /* common limit */
signed char sc = -128;   /* common negative limit */

Будь то char тип следует рассматривать как signed или unsigned по умолчанию зависит от реализации.

В Паскале символы и целые числа являются разными типами. Встроенные функции компилятора ord() и chr() может использоваться для приведения отдельных символов к соответствующему целочисленному значению используемого набора символов и наоборот. например, в системах, использующих набор символов ASCII ord('1') = 49 и chr(9) является символом TAB.

Логические типы

[ редактировать ]

В языке Паскаль логическое значение является перечислимым типом. Возможные значения boolean false и true , с порядковым значением false = 0 и true = 1. Для преобразования в целое число используется : ord

i := ord(b);

Не существует стандартной функции для преобразования целого числа в логическое значение , однако на практике преобразование простое:

b := i <> 0;

C не имеет логического типа. В C используются операторы отношений с двоичными значениями (<, >, ==, !=, <=, >=), которые можно рассматривать как логические в том смысле, что они всегда дают результаты, равные нулю или единице. Поскольку все тесты (&&, ||, ?:, if , while и т. д.) выполняются путем проверки нуля, false представляется нулем, а true — любым другим значением. Это видно в bool числовой тип данных, определенный в stdbool.h.

Побитовые операции

[ редактировать ]

C позволяет использовать побитовые операторы для выполнения логических операций. Необходимо соблюдать осторожность, поскольку семантика меняется, когда операнды используют более одного бита для представления значения.

В Паскале есть еще один, более абстрактный, высокоуровневый метод работы с побитовыми данными — устанавливает . Наборы позволяют программисту устанавливать, очищать, пересекать и объединять побитовые значения данных вместо использования прямых побитовых операторов (которые также доступны в современном Паскале). Пример;

Паскаль:

Status := Status + [StickyFlag];
Status := Status - [StickyFlag];
if (StickyFlag in Status) then ...

(* Alternatively, using bitwise operators: *)
Status := Status or StickyFlag;
Status := Status and not StickyFlag;
if StickyFlag and Status = StickyFlag then ...

С:

Status |= StickyFlag;
Status &= ~StickyFlag;
if (Status & StickyFlag) { ...

Хотя битовые операции над целыми числами и операции над множествами можно считать схожими, если множества реализованы с использованием битов, между их использованием нет прямой параллели, если только не возможно нестандартное преобразование между целыми числами и множествами.

Примечание о реализации

[ редактировать ]

Во время оценки выражения и на обоих языках логическое значение может храниться внутри в виде одного бита, одного байта, полного машинного слова, позиции в сгенерированном коде или в виде кода условия в регистре состояния, в зависимости от машины. , компилятор и ситуация; эти факторы обычно более важны, чем компилируемый язык.

Типы с плавающей запятой

[ редактировать ]

В C существует менее строгая модель типов с плавающей запятой , чем в Pascal. В C целые числа могут быть неявно преобразованы в числа с плавающей запятой и наоборот (хотя возможная потеря точности может быть отмечена предупреждениями). В Паскале целые числа могут быть неявно преобразованы в real, но преобразование real к integer (где информация может быть потеряна) должно быть сделано явно с помощью функций trunc() и round(), которые усекают или округляют дробь соответственно.

Типы перечислений

[ редактировать ]

И C, и Pascal включают типы перечисления. Пример Паскаля:

type
  color = (red, green, blue);
var
  a: color;

Пример переменного тока:

enum color {red, green, blue};
enum color a;

Однако поведение типов в двух языках сильно различается. В Паскале перечисления являются порядковыми и анализируются с помощью ord(), succ() и pred() функции и отличаются от array структура. В C перечисления фактически реализованы как arrays и red становится просто синонимом 0, green за 1, blue для 2, и ничто не мешает присвоить переменной значение вне этого диапазона a. Кроме того, такие операции, как a = a + 1; строго запрещены в Паскале; вместо этого вы бы использовали a := succ(a);. В C перечисления можно свободно преобразовывать в целые числа и обратно, но в Паскале функция ord() должен использоваться для преобразования перечисляемых типов в целые числа, в противоположном преобразовании должна использоваться операция приведения типов, например a := color(1) для green возврат стоимости.

Структурированные типы

[ редактировать ]

Типы массивов

[ редактировать ]

И C, и Pascal допускают использование массивов других сложных типов, включая другие массивы. Однако на этом сходство языков заканчивается. Массивы C просто определяются базовым типом и количеством элементов:

int a[SIZE];

и всегда индексируются от 0 до SIZE-1 (т.е. по модулю SIZE).

В Паскале диапазон индексов часто задается поддиапазоном (как это было представлено выше в разделе простых типов). Десять элементов

var a : array[0..9] of integer;

будет индексироваться от 0 до 9 (как и в данном случае в C). Индексы массива могут быть любым порядковым типом данных , а не только диапазонами:

type
   TColor = (red, green, blue);       (* enumeration *)
   RGB = array[TColor] of 0..255;

var picture : array[1..640, 1..480] of RGB

var palette : array[byte, 0..2] of byte

Строки, состоящие из n (>1) символов, определяются как упакованные массивы с диапазоном 1..n.

Массивы и указатели

[ редактировать ]

В выражениях C идентификатор, представляющий массив, рассматривается как постоянный указатель на первый элемент массива, таким образом, учитывая объявления int a[10] и int *p; задание p = a допустимо и заставляет p и a указывать на один и тот же массив. В качестве идентификатора a представляет собой постоянный адрес, a = p однако недействителен.

Хотя массивы в C фиксированы, указатели на них взаимозаменяемы. Эта гибкость позволяет C манипулировать массивами любой длины, используя один и тот же код. Это также возлагает на программиста ответственность не писать за пределами выделенного массива, поскольку в язык не встроены никакие проверки.

В Паскале массивы представляют собой отдельный тип от указателей. Это делает возможной проверку границ массивов с точки зрения компилятора. компиляции Практически все компиляторы Pascal поддерживают проверку диапазона в качестве опции . Возможность иметь массивы, длина которых изменяется во время выполнения, и возможность проверять их под контролем языка часто называют «динамическими массивами». В языке Паскаль количество элементов в каждом типе массива определяется во время компиляции и не может быть изменено во время выполнения программы. Следовательно, невозможно определить массив, длина которого каким-либо образом зависит от данных программы. (Примечание: с 1986 года и Turbo Pascal 3, который был отраслевым стандартом, GetMem() позволяет использовать динамические массивы в повседневном Паскале, если не в стандарте ISO)

C имеет возможность инициализировать массивы произвольной длины. sizeof Оператор можно использовать для получения размера статически инициализированного массива в коде C. Например, в следующем коде конечный индекс цикла автоматически корректируется при изменении списка строк.

static char *wordlist[] = {
  "print",   "out",   "the",  "text",   "message" };
static int listSize = (sizeof(wordlist)/sizeof(wordlist[0]));
int i;

for (i=0; i<listSize; i++)
  puts(wordlist[i]);
for (i=listSize-1; i>=0; i--)
  puts(wordlist[i]);

Точно так же современный Паскаль, например Delphi и Free Pascal, обладает аналогичной способностью. Инициализированные массивы могут быть реализованы как:

var
  wordlist: array of string = [
    'print', 'out', 'the', 'text', 'message'];
  i: Integer;
begin
  for i := Low(wordlist) to High(wordlist) do
    writeln(wordlist[i]);
  for i := High(wordlist) downto Low(wordlist) do
    writeln(wordlist[i]);
end.

В оригинальном Паскале нет ни инициализации массива (за исключением строк), ни средств определения произвольных размеров массива во время компиляции. Один из способов реализации приведенного выше примера в исходном языке Pascal, но без автоматической регулировки размера:

const
  minlist = 1;
  maxlist = 5;
  maxword = 7;

type
  listrange = minlist .. maxlist;
  wordrange = 1..maxword;
  word = record
    contents: packed array [wordrange] of char;
    length: wordrange
  end;
  wordlist = array[listrange] of word;
var
  i: integer;
  words: wordlist;

procedure CreateList(var w: wordlist);
begin
  w[1].contents := 'print  ';
  w[1].length := 5;
  w[2].contents := 'out    ';
  w[2].length := 3;
  w[3].contents := 'the    ';
  w[3].length := 3;
  w[4].contents := 'text   ';
  w[4].length := 4;
  w[5].contents := 'message';
  w[5].length := 7;
end;

begin
  CreateList(words);
  for i := minlist to maxlist do
    with words[i] do
      WriteLn(contents: length);
  for i := maxlist downto minlist do
    with words[i] do
      WriteLn(contents: length)
end.

В обоих языках строка представляет собой примитивный массив символов.

В Паскале строковый литерал длины n совместим с типом packed array [1..n] of char. В C строка обычно имеет тип char[n].

В Паскале нет поддержки массивов переменной длины, поэтому любой набор процедур для выполнения строковых операций зависит от конкретного размера строки. Теперь стандартизированное расширение Паскаля «соответствующий параметр массива» в значительной степени решает эту проблему, и многие или даже большинство реализаций Паскаля поддерживают строки, присущие этому языку.

Строковые литералы C завершаются нулем ; то есть завершающий нулевой символ как сигнал конца строки :

const char *p;
p = "the rain in Spain";     /* null-terminated */

Нулевое завершение необходимо поддерживать вручную для строковых переменных, хранящихся в массивах (часто это частично обрабатывается библиотечными процедурами).

В C отсутствует встроенное присваивание строк или массивов, поэтому строка не передается в p, а вместо этого p создается так, чтобы указывать на константную строку в памяти.

В Паскале, в отличие от C, первый символьный элемент строки имеет индекс 1, а не 0 (что приводит к тому, что он имеет префикс длины ). Это связано с тем, что Паскаль сохраняет длину строки в нулевом элементе массива символов. Если эта разница не совсем понятна, это может привести к ошибкам при портировании или попытке взаимодействия объектного кода, созданного на обоих языках.

FreeBSD Разработчик Пол-Хеннинг Камп , пишущий в ACM Queue , позже назвал победу строк с нулевым завершением над строками с префиксом длины «самой дорогой однобайтовой ошибкой» за всю историю. [1]

Типы записей

[ редактировать ]

И C, и Pascal могут объявлять « запись типы ». В C они называются «структурами».

struct a {
   int b;
   char c;
};
type a = record
   b: integer;
   c: char;
end;

В Паскале мы можем использовать предложение « with name_of_record do», чтобы напрямую использовать поля этой записи, например локальные переменные, вместо записи name_of_record . имя_поля . Вот пример:

type r = record
   s: string;
   c: char;
end;
var r1 : r;
begin
  with r1 do begin
    s := 'foo';
    c := 'b';
end;

нет эквивалентной функции with В C .

В C можно указать точную длину поля в битах:

struct a {
   unsigned int b:3;
   unsigned int c:1;
};

Объем используемой памяти зависит от особенностей (например, выравнивания слов) целевой системы.

Эта функция доступна в Паскале с помощью конструкции поддиапазона (3 бита дают диапазон от 0 до 7) в сочетании с ключевым словом Packed :

type a = packed record
   b: 0..7;
   c: 0..1;
end;

И C, и Pascal поддерживают записи, которые могут включать в себя различные поля, перекрывающие друг друга:

union a {
   int a;
   float b;
};
type a = record
   case boolean of
      false: (a: integer);
      true:  (b: real)
end;

Оба языковых процессора могут выделить для этих записей ровно столько места, сколько необходимо для размещения наибольшего типа в объединении/записи. В Паскале такие конструкции называются вариантными записями , чтобы не путать их с типом данных Variant , определенным в Free Pascal.

Самая большая разница между C и Pascal заключается в том, что Pascal поддерживает явное использование «поля тега» для языкового процессора, чтобы определить, осуществляется ли доступ к допустимому компоненту вариантной записи:

type a = record
   case q: boolean of
      false: (a: integer);
      true:  (b: real)
end;

В этом случае поле тега q должно быть установлено в правильное состояние для доступа к нужным частям записи.

Указатели

[ редактировать ]

В C указатели могут указывать на большинство объектов программы, включая объекты и функции:

int a;
int *b;
int (*compare)(int c, int d);
int  MyCompare(int c, int d);
 
b = &a;
compare = &MyCompare;

В C, поскольку массивы и указатели имеют близкую эквивалентность, следующие элементы одинаковы:

a = b[5];
a = *(b+5);
a = *(5+b);
a = 5[b];

Таким образом, указатели часто используются в C как еще один метод доступа к массивам.

Для создания динамических данных используются функции библиотеки malloc() и free() используются для получения и освобождения динамических блоков данных. Таким образом, динамическое выделение памяти не встроено в языковой процессор. Это особенно ценно, когда C используется в ядрах операционных систем или встроенных целях, поскольку эти вещи очень специфичны для платформы (а не только архитектуры) и потребуют изменения компилятора C для каждой платформы (или операционной системы), на которой он будет использоваться.

В Паскале есть те же указатели, что и в C, но ^ ссылающийся оператор вместо * из C. Каждый указатель привязан к одному элементу динамических данных и может перемещаться только путем присвоения:

type a = ^integer;

var b, c: a;

new(b);
c := b;

Указатели в Паскале являются типобезопасными; т.е. указатель на один тип данных может быть присвоен только указателю того же типа данных. Кроме того, указатели никогда не могут быть назначены переменным, не являющимся указателями. Арифметика указателей (частый источник ошибок программирования на C, особенно в сочетании с проблемами порядка байтов и размерами типов, независимыми от платформы) не разрешена в Паскале. Все эти ограничения уменьшают вероятность ошибок, связанных с указателями, в Паскале по сравнению с C, но не предотвращают полностью недопустимые ссылки на указатели в Паскале. Например, ошибка времени выполнения произойдет, если на указатель будет сделана ссылка до его инициализации или после его удаления.

Выражения

[ редактировать ]

Уровни приоритета

[ редактировать ]

Языки существенно различаются, когда дело доходит до оценки выражений, но в целом они сопоставимы.

Паскаль

  1. Логическое отрицание: not
  2. Мультипликативный: * / div mod and
  3. Добавка: + - or
  4. Реляционный: = <> < > <= >= in

С

  1. Унарный постфикс: [] () . -> ++ --
  2. Унарный префикс: & * + - ! ~ ++ -- (type) sizeof
  3. Мультипликативный: * / %
  4. Добавка: + -
  5. Сдвиг: << >>
  6. Реляционный: < > <= >=
  7. Равенство: == !=
  8. Побитовое и: &
  9. Побитовое ИЛИ: ^
  10. Побитовое или: |
  11. Логично и: &&
  12. Логично или: ||
  13. Условно: ? :
  14. Назначение: = += -= *= /= %= <<= >>= &= ^= |=
  15. Оператор запятая : ,

Ввод текста

[ редактировать ]

Большинство операторов в Паскале служат нескольким целям, например, знак минус может использоваться для отрицания, вычитания или установки разницы (в зависимости как от типа, так и от синтаксического контекста). >= Оператор может использоваться для сравнения чисел, строк или наборов и т. д. C в большей степени использует специальные символы операторов.

Тесты присваивания и равенства

[ редактировать ]

В двух языках для присваивания используются разные операторы. Паскаль, как и АЛГОЛ , использует оператор математического равенства. = для проверки равенства и символа := для присваивания, тогда как C, как и B , использует для присваивания оператор математического равенства. В C (и B) == символ ФОРТРАНа для проверки равенства был выбран .

Распространенной ошибкой в ​​языке C, вызванной либо неопытностью, либо простой ошибкой ввода, является случайное помещение выражений присваивания в условные операторы, такие как if (a = 10) { ... }. Код в фигурных скобках всегда будет выполняться, поскольку выражение присваивания a = 10 имеет значение 10, которое не равно нулю и поэтому считается «истинным» в C; отчасти это связано с тем, что C (и ALGOL) допускают множественное присваивание в форме a = b = c = 10; который не поддерживается Паскалем. Также обратите внимание, что a теперь имеет значение 10, что может повлиять на следующий код. Последние компиляторы C пытаются обнаружить такие случаи и предупреждать пользователя, запрашивая менее двусмысленный синтаксис, например if ((a=10) != 0 ) { ... }.

Такого рода ошибки не могут произойти в Паскале, поскольку присваивания не являются выражениями и не имеют значения: использование неправильного оператора приведет к однозначной ошибке компиляции, а также менее вероятно, что кто-то ошибется в := символ проверки равенства.

Примечательно, что условное выражение АЛГОЛА в форме z := if a > b then a else b; имеет эквивалент в C ( тройной оператор из CPL ), но не в Паскале, где будет использоваться if a > b then z:=a; else z:=b;.

Проблемы реализации

[ редактировать ]

Когда Никлаус Вирт разрабатывал Паскаль, желанием было ограничить количество уровней приоритета (в конце концов, меньше процедур синтаксического анализа). Таким образом, операторы OR и исключающее OR обрабатываются так же, как Addop, и обрабатываются на уровне математического выражения. Аналогично, оператор AND рассматривается как Mulop и обрабатывается с помощью Term. Уровни приоритета

Уровень Элемент синтаксиса Оператор
0 фактор буквальный, переменный
1 знаковый фактор унарный минус, НЕТ
2 срок *, /, И
3 выражение +, -, ИЛИ

Обратите внимание, что существует только ОДИН набор синтаксических правил, применимых к обоим типам операторов. Таким образом, согласно этой грамматике, выражения типа

     x + (y AND NOT z) / 3

совершенно законны. И на самом деле так оно и есть с точки зрения парсера. Паскаль не позволяет смешивать арифметические и логические переменные, и подобные вещи улавливаются на семантическом уровне, когда приходит время генерировать для них код, а не на уровне синтаксиса.

Авторы C использовали диаметрально противоположный подход: они рассматривают операторы как разные, а на самом деле в C не менее 15 уровней. Это потому, что в C также есть операторы '=', '+=' и подобные им '<<', '>>', '++', '--' и т. д. Хотя в C арифметические и логические операторы рассматриваться отдельно, переменные — нет: логический тест можно выполнить для любого целочисленного значения.

Логические связки

[ редактировать ]

В Паскале логическое выражение, основанное на определенном порядке вычислений (возможно, из-за побочных эффектов при вызове функций), в той или иной степени рассматривается как ошибка. Компилятор Паскаля имеет право использовать любой порядок, который он предпочитает, и всегда должен вычислять все выражение целиком, даже если результат может быть определен путем частичного вычисления. (Примечание: начиная с Turbo Pascal 3 (1986), сокращенное логическое вычисление доступно в повседневном Паскале, если не в стандарте ISO).

В C зависимость от логического порядка вычислений совершенно законна и часто систематически используется с использованием && и || операторы вместе с такими операторами, как ++, +=, оператор запятая и т. д. && и || таким образом, операторы функционируют как комбинации логических операторов и условных операторов .

Вычисление выражений короткого замыкания обычно считается преимуществом C из-за «проблемы вычисления»:

var i: integer;
    a: packed array [1..10] of char;
  
  ...
  i := 1;
  while (i <= 10) and (a[i] <> 'x') do i := i+1;
  ...

Этот, казалось бы, простой поиск проблематичен в Паскале, поскольку доступ к массиву a[i] будет недействителен для i, равного 11. Существует несколько способов избежать этой проблемы. В следующем примере представлена ​​логическая переменная, которая указывает, найден ли целевой символ:

const
  strlen = 10;
var i: integer;
    a: packed array [1..strlen] of char;
    found: boolean;
  
  ...
  i := 1;
  found := false;
  while not found and (i <= strlen) do
    if (a[i] = 'x') then found := true else i := i+1;
  ...

Альтернативно, проверка конца массива может быть отделена от доступа к массиву, и оператор goto может прервать поиск, если цель найдена:

label 99;
const
  strlen = 10;
var i: integer;
    a: packed array [1..strlen] of char;
  
  ...
  i := 1;
  repeat
    if a[i] = 'x' then goto 99;
    i := i+1
    until i > strlen;
  99: 
  ...

Структуры управления

[ редактировать ]

Положения для структур управления строительством примерно аналогичны и относительно схожи (по крайней мере, первые три).

Паскаль С
if cond then stmt else stmtif (cond) stmt else stmt
while cond do stmtwhile (cond) stmt
repeat stmt until conddo stmt while (cond);
for id := expr to expr do stmt
и
for id := expr downto expr do stmt
for (expr; cond; expr) stmt
case expr of
    expr : stmt;
    ...
    expr : stmt;
    else: stmt;
end
switch (expr) {
    case expr : stmt;
    ...
    case expr : stmt;
    default: stmt
}

Паскаль в своей первоначальной форме не имел эквивалента по умолчанию , но эквивалент else Предложение является общим расширением. В противном случае программистам на Паскале приходилось защищать операторы регистра такими выражениями, как: если выражение не в [A..B], тогда регистр по умолчанию .

В C есть так называемые операторы раннего выхода. перерыв и continue , и они есть и в некоторых Паскалях.

И C, и Pascal имеют оператор перехода . Однако, поскольку в Паскале есть вложенные процедуры/функции, можно выполнять переходы от внутренней процедуры или функции к содержащей их; это обычно использовалось для реализации восстановления ошибок. C имеет эту возможность через ANSI C. setjmp и лонгджмп . Это эквивалентно, но, возможно, менее безопасно, поскольку сохраняет специфичную для программы информацию, такую ​​как адреса перехода и кадры стека, в структуре, доступной программисту.

Функции и процедуры

[ редактировать ]

Подпрограммы Паскаля, возвращающие значение, называются функциями; подпрограммы, которые не возвращают значение, называются процедурами. Все процедуры в C называются функциями; Функции C, которые не возвращают значение, объявляются с типом возврата void .

Процедуры Паскаля считаются эквивалентными функциям C «void», а функции Паскаля эквивалентны функциям Си, возвращающим значение.

Следующие два объявления на языке C:

int f(int x, int y);
void k(int q);

эквивалентны следующим объявлениям в Паскале:

function f(x, y: integer): integer;
procedure k(q: integer);

В Паскале есть два разных типа параметров: передача по значению и передача по ссылке (VAR). В обоих случаях при вызове используется имя переменной (нет необходимости в операторе адреса).

function f(z: integer; var k: integer): integer; // function accepts two integers, one by value, one by reference
Begin
  z:=1; // outer variable u will not be modified, but local value is modified in the function's scope
  k:=1; // outer variable t will be modified because it was passed by reference
  // up to here, z exists and equals 1
End;

x := f(u,t); // the variables u and t are passed to the call : the value of u and the reference to t

В C все параметры передаются по значению, но передачу по ссылке можно моделировать с помощью указателей. Следующий сегмент аналогичен приведенному выше сегменту Pascal:

int f(int z, int *k) { //function accepts an int (by value) and a pointer to int (also by value) as parameter
  z=1;  // idem Pascal, local value is modified but outer u will not be modified
  *k=1; // variable referenced by k (eg, t) will be modified
  // up to here, z exists and equals 1
}

x = f(u,&t); // the value of u and the (value of) address of variable t are passed to the call

Одним из наиболее важных различий между C и Pascal является способ обработки параметров в стеке во время вызова подпрограммы: Это называется соглашением о вызовах : Параметры в стиле PASCAL помещаются в стек слева направо. Соглашение о вызовах STDCALL языка C помещает параметры в стек в порядке справа налево.

Вызов процедуры в стиле Паскаля осуществляется с помощью:

  • вызывающий объект помещает параметры в стек в порядке слева направо (в противоположность __cdecl )
  • вызов функции
  • стек очищается вызываемым абонентом
    ; example of pascal-style call.
    ; NOTE: __stdcall would push the arguments in reverse order.
    push arg1
    push arg2
    push arg3
    call function
    ; no stack cleanup upon return: callee did it

Преимущество вызова PASCAL перед STDCALL заключается в том, что код немного меньше, хотя влияние размера заметно только в больших программах, и что рекурсия работает быстрее.

Функции с переменным числом аргументов практически невозможно правильно реализовать с помощью методов PASCAL и STDCALL, поскольку только вызывающий объект действительно знает, сколько аргументов было передано для их очистки.

C позволяет функциям принимать переменное количество параметров, известных как вариативные функции , используя неуклюжий механизм va_list ap;, va_start(ap, count);, va_arg(ap, type); с ограниченной доступностью типов (пример: ничего для bool)

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

Функция f() использует специальный набор функций ( varargs), которые позволяют ему получить доступ к каждому из параметров по очереди.

В Паскале и C также есть некоторые функции ввода-вывода с переменным числом вариантов, например WriteLn() и printf().

Современные Паскали допускают переменное количество параметров для функций:

procedure writeLines(const arguments: array of const); // parsed via : for argument in arguments do

Они также позволяют взаимодействовать с функциями C с переменными аргументами:

Function PrintF1(fmt : pchar); cdecl; varargs;  external 'c' name 'printf';

Паскаль позволяет вкладывать процедуры и функции . Это удобно, поскольку переменные являются локальными для группы процедур, но не глобальными. В C отсутствует эта возможность, и локализацию переменных или функций можно выполнить только для компилирующего модуля, в котором переменные или функции были бы объявлены статическими .

C и Pascal позволяют вызывать функции косвенно через указатель на функцию . В следующем примере утверждение (*cmpar)(s1, s2) эквивалентно strcmp(s1, s2):

#include <string.h>

int (*cmpar)(const char *a, const char *b);
const char *s1 = "hello";
const char *s2 = "world";

cmpar = &strcmp;
b = (*cmpar)(s1, s2);

В Паскале функции и процедуры могут передаваться в качестве параметров функциям или процедурам:

procedure ShowHex(i: integer);
...
end;

procedure ShowInt(i: integer);
...
end;

procedure Demo(procedure Show(i: integer));
var j: integer;
begin
  Show(j)
end;

...
  Demo(ShowHex);
  Demo(ShowInt);
...

Препроцессор

[ редактировать ]

В раннем языке C не было ни объявлений констант, ни объявлений типов, а язык C изначально определялся как нуждающийся в « препроцессоре »; отдельная программа и проход, который обрабатывает определения констант, включений и макросов , чтобы снизить использование памяти. Позже, с появлением ANSI C, он получил функции определения констант и типов, а препроцессор также стал частью языка, что привело к синтаксису, который мы видим сегодня.

Определения констант и типов Паскаля встроены и не требуют препроцессора. Были программисты, использующие препроцессор также с Паскалем (иногда тот же самый, который использовался с C), конечно, не так часто, как с C. Хотя его часто называют «недостатком» в Паскале, технически C не имеет модульности программы и встроенных макросов. или. Однако у него есть простая возможность отдельной низкоуровневой компиляции (традиционно использующая тот же универсальный компоновщик, что и для языка ассемблера), а в Паскале его нет.

Введите escape-последовательность

[ редактировать ]

В C программист может проверить представление любого объекта на уровне байтов, указав char указатель на него:

int a;
char *p = (char *)(&a);
char c = *p;  // first byte of a

Возможно, что-то подобное можно сделать в Паскале, используя нераспознаваемую вариантную запись:

var a: integer;
    b: real;
    a2c: record
           case boolean of
             false: (a: integer);
             true:  (b: real);
           end;
         end;
begin
  a2c.b := b;
  a := a2c.a;
end;

Хотя приведение возможно в большинстве компиляторов и интерпретаторов Pascal, даже в приведенном выше коде a2c.a и a2c.b не требуют каких-либо стандартизаций Pascal, чтобы они использовали одно и то же адресное пространство. Никлаус Вирт, разработчик Паскаля, писал о проблематичности попыток экранирования типов с использованием этого подхода:

«Большинство разработчиков Паскаля решили, что такая проверка будет слишком дорогой, приведет к увеличению кода и ухудшению эффективности программы. Как следствие, вариантная запись стала излюбленной функцией нарушения системы типов всех программистов, любящих трюки, которые обычно оборачиваются ловушками. и бедствия».

Некоторые языки теперь специально исключают такие escape-типы, например Java, C# и собственный Oberon от Wirth .

В C файлы не существуют как встроенные типы (они определены в системном заголовке), и весь ввод-вывод осуществляется посредством вызовов библиотеки. В языке Паскаля встроена обработка файлов.

Типичные операторы, используемые для выполнения ввода-вывода на каждом языке:

printf("The sum is: %d\n", x);
writeln('The sum is: ', x);

Основное отличие состоит в том, что C использует «строку формата», которая интерпретируется для поиска аргументов функции printf и их преобразования, тогда как Паскаль выполняет это под управлением языкового процессора. Метод Паскаля, возможно, быстрее, поскольку не происходит никакой интерпретации, но метод C обладает высокой расширяемостью.

Более поздние реализации и расширения Паскаля

[ редактировать ]

Некоторые популярные реализации Паскаля включили в Паскаль практически все конструкции C. Примеры включают приведение типов, [2] возможность получить адрес любой переменной, локальной или глобальной, а также целых чисел различных типов со специальными свойствами продвижения.

Однако включение снисходительного отношения C к типам и преобразованиям типов может привести к тому, что Паскаль потеряет часть или всю свою безопасность типов. Например, Java и C# были созданы частично для решения некоторых предполагаемых проблем безопасности типов C и имеют «управляемые» указатели, которые нельзя использовать для создания недопустимых ссылок. В своей первоначальной форме (как описано Никлаусом Виртом ) Паскаль квалифицируется как язык управляемых указателей, примерно на 30 лет раньше, чем Java или C#. Однако Pascal, объединенный с C, потеряет эту защиту по определению. В целом, меньшая зависимость от указателей для основных задач делает его на практике более безопасным, чем C.

Стандарт Extended Pascal расширяет Pascal для поддержки многих вещей, поддерживаемых C, которых не было в исходном стандарте Pascal, более безопасным типом. Например, типы схем поддерживают (помимо других применений) массивы переменной длины, сохраняя при этом безопасность типов, заключающуюся в обязательном переносе измерения массива вместе с массивом, что позволяет автоматически проверять во время выполнения индексы, выходящие за пределы диапазона, также для массивов с динамическим размером.

См. также

[ редактировать ]

Примечания

[ редактировать ]
  1. ^ Камп, Пол-Хеннинг (25 июля 2011 г.), «Самая дорогая однобайтовая ошибка» , ACM Queue , 9 (7): 40–43, doi : 10.1145/2001562.2010365 , ISSN   1542-7730 , S2CID   30282393 , получено 2 августа 2011 год
  2. ^ «Typecast — Lazarus wiki» . wiki.freepascal.org . Проверено 18 мая 2024 г.

Дальнейшее чтение

[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 8d552a988a999747a0cac775cf3d7040__1721779080
URL1:https://arc.ask3.ru/arc/aa/8d/40/8d552a988a999747a0cac775cf3d7040.html
Заголовок, (Title) документа по адресу, URL1:
Comparison of Pascal and C - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)