Из Википедии, бесплатной энциклопедии
Перейти к навигации Перейти к поиску

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

И C, и Pascal являются старыми языками программирования: исходное определение Pascal появилось в 1969 году, а первый компилятор - в 1970 году. Первая версия C появилась в 1972 году. Хотя C не сильно изменился со временем, Pascal сильно изменился, и в настоящее время подавляющее большинство программирования на Паскале выполняется в современном Object Pascal , а не в старом процедурном Pascal. Старый процедурный Pascal сегодня по существу ограничен программированием микроконтроллеров с помощью таких инструментов, как mikroPascal , в то время как Object Pascal является основным диалектом и используется с такими инструментами, как Delphi , Lazarus (IDE) и Free Pascal .

Здесь задокументирован современный Object Pascal, используемый во Free Pascal и Delphi. Документированный C - это C99, стандартизированный в 1999 году.

Синтаксис [ править ]

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

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

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

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

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

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

В традиционном C есть только /* block comments */. Начиная с C99, существуют также файлы //Line comments . В Object Pascal, есть , и .{ block comments }(* block comments *)// Line comments

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

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

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

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

Определения, объявления и блоки [ править ]

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

В функциях 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будут как минимум двумя байтами.

Поддиапазоны [ править ]

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

введите  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(восьмибитный байт на большинстве машин) ясно иллюстрируется такими объявлениями, как

беззнаковый  символ  uc  =  255 ;  / * общий лимит * / signed  char  sc  =  -128 ;  / * общий отрицательный предел * /

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

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

В дополнение к Charтипу Object Pascal также должен WideCharпредставлять символы Unicode. В C это обычно реализуется как макрос или typedefс именем wchar_t, которое является просто псевдонимом для int.

Логические типы [ править ]

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

i  : =  ord ( b ) ;

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

b  : =  логическое значение ( i ) ;  // Будет вызывать правильные ошибки проверки диапазона для неопределенных значений при включенной проверке диапазона.

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

Для того, чтобы взаимодействовать с COM, Object Pascal добавил ByteBool, WordBoolи LongBoolтип, размер которого уважает их префикс и следовать таблице истинности С.

Free Pascal добавил соответствующие логические типы Паскаля с суффиксом размера ( boolean8, 16, 32, 64) для взаимодействия с GLIB, который использует gboolean32-битный логический тип с таблицей истинности Паскаля.

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

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

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

Паскаль:

Статус  : =  Статус  +  [ StickyFlag ] ;  // или Включить (Статус, StickyFlag); Статус  : =  Статус  -  [ StickyFlag ] ;  // или исключить (Статус, StickyFlag); если  ( StickyFlag  в  статусе ),  то  ...

C:

Статус  | =  StickyFlag ; Статус  & =  ~ StickyFlag ; if  ( Status  &  StickyFlag )  {  ...

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

Паскаль может также сделать битовые операции точно так же, как и C за счет использования and, or, notи xorоператоров. Эти операторы обычно работают с логическими значениями, но когда операнды являются целыми числами, они действуют как побитовые операторы. Это стало возможным благодаря тому, что логические и целые числа являются разными несовместимыми типами. Следовательно, приведенный выше код C может быть записан на Паскале как:

Статус  : =  Статус  или  StickyFlag ; Статус  : =  Статус,  а  не  StickyFlag ; если  Status  и  StickyFlag  <>  0,  то  ...

Расширенные типы [ править ]

Тип строки [ править ]

В C строка остается указателем на первый элемент массива char с нулевым завершением, как это было в 1972 году. <string.h>Для управления строками по- прежнему необходимо использовать библиотечную поддержку from .

Object Pascal имеет много строковых типов, потому что, когда вводится новый тип, старый сохраняется для обратной совместимости. Это произошло дважды, один раз с Delphi 2 (введение ansistring) и Delphi 2009 (Unicodestring). Помимо основных строковых типов (short-, ansi-, wide-, unicodestring) и соответствующих символьных типов (ansichar, widechar = unicodechar), все типы, производные от символьного типа, также имеют некоторые строковые свойства (указатель на char, массив символов , динамический массив символов, указатель на массив символов и т. д.).

В Object Pascal stringэто управляемый компилятором тип и подсчет ссылок (если он должен быть), то есть управление его хранилищем обрабатывается компилятором (или, точнее, кодом времени выполнения, вставленным компилятором в исполняемый файл) . Объединение строк осуществляется с помощью +оператора, и сравнение строк может быть сделано с помощью стандартных реляционных операторов (чувствительно к регистру): < <= = <> >= >.

Object Pascal также предоставляет C-совместимые строки под типом PAnsiCharс процедурами манипуляции, определенными в Stringsмодуле. Кроме того, Object Pascal предоставляет широкий спектр типов строк:

  • ShortString, который внутренне является
    массив  [ 0  ..  N ]  из  Чар ;
    где N - максимальное количество символов, которое может быть сохранено, и 0-й индекс, содержащий длину строки. Максимально 255 символов могут быть сохранены в a ShortString, потому что верхний предел беззнакового байта равен 255, а массив контейнера определен так, чтобы иметь данные максимум 255 символов (помните, что 0-й индекс содержит длину строки). N задается либо в определении типа, либо в объявлении переменной (см. Пример ниже)
  • AnsiString, динамическая версия с неограниченной длиной и подсчетом ссылок ShortString. Начиная с Delphi 2009, в нем есть поле, которое сигнализирует о кодировке содержимого.
  • WideString, в Windows (win32 / 64 / ce), совместимом с COM BSTR, UCS2 / UTF16, пересчитывается с помощью COM. В системах, отличных от Windows, равно Unicodestring.
  • UnicodeStringвроде бы WideString, но в кодировке UTF-16

Для удобства предоставляется простой Stringтип, который, в зависимости от параметра компилятора, может означать ShortString, AnsiStringили UnicodeString. Используется дополнительное соглашение: если задано ограничение на количество символов, это a ShortString, в противном случае - другое.

Short-и Ansi-Струны можно свободно перемешивать при манипулировании струнами; при необходимости компилятор выполнит преобразование без вывода сообщений. Обратите внимание, что если тип целевой строки - ShortStringтихое усечение может произойти из-за максимально допустимой длины.

Пример:

тип  TString80  =  String [ 80 ] ; var  ss  :  ShortString ;  s80  :  Строка [ 80 ] ;  // объявляем (короткую) строку максимальной длины 80  s80t :  TString80 ;  // то же, что и выше  astr :  AnsiString ;  s  :  String ;  // может означать String [255], AnsiString или UnicodeString begin  ss  : =  astr  +  s80 ; // ДА, это возможно, и преобразование выполняется прозрачно компилятором end ;

Тип массива [ править ]

Статический массив [ править ]

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

// объявляем int "массив" с именем a длиной 10 int  a [ 10 ]; // выводим первый элемент, а точнее элемент по адресу, удерживаемому a + 0 printf ( "% d" , a [ 0 ]); // выводим второй элемент, а точнее элемент по адресу, удерживаемому + 1 printf ( "% d" , a [ 1 ]); // передать массив в функцию, а точнее передать указатель на первый элемент somefunction ( a ); // то же, что и выше somefunction ( & a [ 0]);

Чтобы получить длину массива, нужно вычислить . Поэтому, чтобы подсчитать длину массива целых чисел, используйте: . Распространенная ошибка - вычислять это в функции, ожидающей в качестве аргумента массив. Несмотря на свой внешний вид, функция может принимать в качестве аргумента только указатель, а не реальный массив. Следовательно, внутри функции массив рассматривается как простой указатель. Пример:sizeof(<array_variable>) / sizeof(<base_type>)sizeof(intarr) / sizeof(int)

// Эта функция НЕ принимает массив, но указатель на int // Семантически это то же самое, что: int * a void  func ( int  a [])  {  // НЕПРАВИЛЬНО! Вернет sizeof (указатель) / sizeof (int)  int  len  =  sizeof ( a )  /  sizeof ( int ); }int  main ()  {  int  a [ 5 ];  // правильно, вернет 5  int  len  =  sizeof ( a )  /  sizeof ( int );  func ( а );  возврат  0 ; }

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

Несмотря на то, что он рассматривается как указатель, не все конструкции стиля указателя могут использоваться для создания массива. Например, этот код будет нормально компилироваться, но вызовет нарушение прав доступа при выполнении:

void  func ( int  * a )  {  // ОШИБКА РАБОТЫ! a выделяется статически  a  =  ( int * )  malloc ( sizeof ( int )  *  10 ); }int  main ()  {  int  a [ 5 ];  func ( а ); }

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

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

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

тип  T10IntegerArray  =  массив  [ 1  ..  10 ]  из  Integer ;  TNegativeLowerBoundArray  =  массив  [ - 5  ..  5 ]  из  Integer ;  TNamedIndexTypeArray  =  массив  [ Low ( Char )  ..  High ( Char )]  of  Integer ; var  IntegerArray :  T10IntegerArray ;  NegArray :  TNegativeLowerBoundArray ; NamedIndexTypeArray :  TNamedIndexTypeArray ;

Массивы знают свои верхние и нижние границы (и неявно их длину), и границы передаются, когда функция ожидает массив в качестве аргумента. Функции Low(), High()и Length()получить нижнюю границу, верхняя граница и длина массива, соответственно, в любом контексте.

Без явного приведения массивы не могут и не будут преобразованы в указатели, и это ошибка времени компиляции. Это свойство типобезопасного программирования.

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

тип  TArray1  =  массив  [ 1  ..  10 ]  из  Integer ;  TArray2  =  массив  [ 1  ..  5 ]  из  Integer ; var  a , b :  TArray1 ;  c :  TArray2 ; начать  a  : =  b ;  // OK  // Копируем все элементы из c в a, перезаписывая элементы от 1-го индекса до 1-го индекса + Length (c)  Move ( c , a , Length( c )  *  SizeOf ( Целое число )) ;  // Копируем все элементы из c в a, начиная с индекса 5  Move ( c , a [ 5 ] , Length ( c )  *  SizeOf ( Integer )) ;  // Копируем первые 5 элементов из b в c  Move ( b , c , 5  *  SizeOf ( Integer )) ; конец .

Динамический массив [ править ]

В C нет языковой поддержки для объявления и использования динамических массивов. Однако из-за синтаксиса разыменования указателя динамический массив может быть реализован с помощью функций управления памятью, обычно из <stdlib.h>. Пример:

int  size  =  10 ; int  * a  =  ( int * )  malloc ( sizeof ( int )  *  size );  // размещаем динамический массив целых чисел размером 10 int  i ;for  ( i  =  0 ;  i  <  size ;  i ++ )  ...  // делаем что-нибудь с a [i]размер  * =  2 ; int  * temp  =  realloc ( a , sizeof ( int )  *  размер );  // удваиваем пробел, сохраняя существующие элементы if  ( temp  ==  NULL )  error ( "Недостаточно памяти!" ); а  =  темп ; ...  // делаем что-нибудь с бесплатным ( a );  // освобождаем хранилище

Как можно видеть, длина снова не поддерживается автоматически, и при перераспределении следует использовать дополнительную переменную для защиты от ошибки недостаточного объема памяти. Назначение между динамическими массивами следует правилу присвоения указателя.

Object Pascal обеспечивает поддержку динамических массивов на уровне языка. Он объявлен без указания верхней и нижней границы. Затем необходимо вызвать SetLength()функцию для выделения памяти. Динамические массивы в Object Pascal подсчитываются по ссылкам, поэтому не нужно беспокоиться об освобождении памяти. Динамические массивы всегда начинаются с нуля. Эти три функции Low(), High()и Length()будет по- прежнему получать нижняя граница, верхняя граница и длина массива правильно. Пример:

тип  TIntArray  =  массив  из  Integer ;  T2DimIntArray  =  массив  из  массива  из  Integer ; var  a  :  TIntArray ;  a2  :  T2DimIntArray ;  i , j :  целое число ; begin  SetLength ( a , 10 ) ;  // выделяем 10 хранилищ  для  i  : =  Low ( a )  to  High ( a )  do ...  // что-то делаем с [i]  SetLength ( a2 , 10 , 10 ) ;  // выделяем память 10 x 10  для  i  : =  Low ( a2 )  to  High ( a2 )  do  for  j  : =  Low ( a2 [ i ])  to  High ( a2 [ i ])  do  ...  // делаем что-нибудь с a [i, j] конец ;

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

тип  TIntegerArray  =  массив  из  Integer ; var  a , b :  TIntegerArray ; begin  ...  // инициализируем a и b  a  : =  b ;  // a теперь указывает на тот же массив, на который указывает b  a [ 1 ]  : =  0 ;  // b [1] также должно быть 0 после этого  a  : =  Copy ( b , 3 , 5 ) ;  // Копируем 5 элементов из b, начиная с индекса 3  // a будет обращаться к нему от 0 до 4, но в конце.

Дальнейшее чтение [ править ]

  • Free Pascal: Справочник по языку [1]