Сравнение Паскаля и Си - Comparison of Pascal and C

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

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

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

Синтаксис

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

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

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

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

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

Комментарии

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

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

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

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

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

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

В Паскале определения процедур начинаются с ключевых слов процедура или функция, а определения типов - с типа . В C определения функций определяются синтаксическим контекстом, в то время как определения типов используют ключевое слово typedef. Оба языка используют сочетание ключевых слов и знаков препинания для определений сложных типов; например, массивы определяются ключевым словом array в Pascal и пунктуацией в C, а перечисления определяются ключевым словом enumC, но пунктуацией в Pascal.

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

Реализация

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

Простые типы

Целые числа

Паскаль требует, чтобы все объявления переменных и функций явно указывали их тип. В традиционном C имя типа может быть опущено в большинстве контекстов, а затем неявно предполагается тип по умолчанию int(который соответствует integerв Pascal) (однако такие значения по умолчанию считаются плохой практикой в ​​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 et al. позволили относительно свободно смешивать целочисленные типы и типы указателей .

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

В 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 - это перечислимый тип. Возможные значения boolean - false и true , с порядковым значением false = 0 и true = 1. Для преобразования в целое число используется ord :

i := ord(b);

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

b := i <> 0;

C имеет бинарные операторы отношения (<,>, ==,! =, <=,> =), Которые можно рассматривать как логические в том смысле, что они всегда дают результат, равный нулю или единице. Поскольку все тесты (&&, ||,?:, If , while и т. Д.) Выполняются с помощью проверки нуля, false представлено нулем, а true - любым другим значением.

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

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 ...

C:

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

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

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

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

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

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

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

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

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

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

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

Однако поведение типов на двух языках сильно различается. В C redстановится синонимом 0, green1, blue2, и ничто не мешает присвоить переменной значение вне этого диапазона 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 манипулировать массивом любой длины, используя один и тот же код. Это также оставляет программисту ответственность не писать за пределами выделенного массива, поскольку в язык не встроены проверки.

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

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]);

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

Один из способов реализации приведенного выше примера на Паскале, но без автоматической настройки размера:

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 (что приводит к префиксу длины ). Это потому, что Паскаль хранит длину строки в 0-м элементе массива символов. Если это различие не совсем понятно, оно может привести к ошибкам при портировании или попытке интерфейса объектного кода, созданного обоими языками.

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

Типы записей

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

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

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

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

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

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

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

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

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

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;

Оба языковых процессора могут выделять для этих записей ровно столько места, сколько необходимо для хранения наибольшего типа в объединении / записи.

Самая большая разница между 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

C

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

Набор текста

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

Присваивание и тесты на равенство

В двух языках используются разные операторы для присваивания. Паскаль, как и ALGOL , использует оператор математического равенства =для проверки равенства и символ :=для присваивания, тогда как 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 ) { ... }.

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

Примечательно, что условное выражение ALGOL в форме a := if a > b then a else b;имеет эквивалент в C, но не в Паскале.

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

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

          Level   Syntax Element     Operator

          0       factor             literal, variable
          1       signed factor      unary minus, NOT
          2       term               *, /, AND
          3       expression         +, -, OR

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

     x + (y AND NOT z) / 3

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

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

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

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

В 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;
  ...

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

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

Паскаль имеет:

  • если cond, то stmt else stmt
  • в то время как cond do stmt
  • повторять stmt до cond
  • для id  : = expr to expr do stmt и для id  : = expr downso expr do stmt
  • case expr of expr : stmt ; ... выражение : stmt ; иначе: stmt ; конец

C имеет:

  • если ( условие ) stmt else stmt
  • в то время как ( cond ) stmt
  • делать stmt while ( cond );
  • for ( expr ; cond ; expr ) stmt
  • переключатель ( выражение ) { case expr : stmt ; ... case expr : stmt ; по умолчанию: stmt }

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

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

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

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

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

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

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

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

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

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

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

function f(var k: integer): integer;
x := f(t);

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

int f(int *k); //function accepts a pointer as parameter
x = f(&t);

C позволяет функциям принимать переменное количество параметров, известных как вариативные функции .

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

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

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

C позволяет косвенно вызывать функции через указатель на функцию . В следующем примере оператор (*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 изначально определялся как требующий « препроцессора »; отдельная программа и проход, который обрабатывает определения констант, include и макросов , чтобы уменьшить использование памяти. Позже, с ANSI C, он получил функции определений констант и типов, а препроцессор также стал частью языка, что привело к синтаксису, который мы видим сегодня.

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

Тип экранирования

В 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;

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

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

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

Файлы

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

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

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

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

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

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

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

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

Смотрите также

Примечания

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