コンピュータプログラミング言語 CとPascal は、その誕生時期、影響、目的が似ています。どちらも、その誕生当初は独自のコンパイラを設計 (およびコンパイル) するために使用されていました。オリジナルの Pascal 定義は 1969 年に登場し、最初のコンパイラは 1970 年に登場しました。C の最初のバージョンは 1972 年に登場しました。
どちらもALGOL言語シリーズの子孫です。ALGOL は構造化プログラミングに対するプログラミング言語サポートを導入しました。構造化プログラミングでは、プログラムはif、while、for、caseなどの単一のエントリと単一の終了の構文で構成されます。Pascal はALGOL Wから直接派生したものですが、 ALGOL 68といくつかの新しいアイデアを共有しています。C 言語は、最初はB、BCPL、CPLstructを介して、後に ALGOL 68 (との場合など) や Pascal ( 列挙型、 、 ブール値の場合など) を介して、より間接的に ALGOL に関連しています。union一部の Pascal 方言には、C の特性も組み込まれています。
consttypedef
ここで文書化されている言語は、1982 年に ISO 7185 として標準化されたNiklaus Wirthの Pascal と、1989 年に標準化されたBrian KernighanとDennis Ritchieの C です。これは、これらのバージョンが両方とも言語の成熟バージョンを表しており、また比較的時間的に近いためです。ANSI CとC99 (後の C 標準) の機能、および Pascal の後の実装 ( Turbo Pascal、Free Pascal ) の機能は、堅牢性と機能性が向上しているにもかかわらず、比較には含まれていません。
構文
構文的には、Pascal はCよりも ALGOL に似ています。C が句読点記号を使用するところでは英語のキーワードが保持されます。たとえば、C がand、or、 をmod使用するところでは、Pascal には&&、||、 が%あります。ただし、(単純な)宣言に関しては C は Pascal よりも ALGOL に似ており、型名 変数名の構文を保持しています。たとえば、C は関数の外側のブロックだけでなく、任意のブロックの先頭で宣言を受け入れることができます。
セミコロンの使用
もう 1 つの、より微妙な違いは、セミコロンの役割です。Pascal では、セミコロンは複合文内の個々の文を区切りますが、C では文を終了します。C では、セミコロンは文の構文の一部でもあります (式を文に変換します)。この違いは、主に次の 2 つの状況で現れます。
- Pascalではセミコロンは の直前に置くことはできませんが
else、Cではブロック文が使用されない限りはセミコロンは必須です。 endorの前の最後の文の後untilにセミコロンを続ける必要はありません。
余分なセミコロンをend の前の最後の行に置くことで、正式に空のステートメントを挿入することができます。
コメント
従来の C では、 のみです/* block comments */。これは、MIDletPascal などの特定の Pascal 方言でのみサポートされています。
伝統的なPascalには、とがあります。Object Pascal(Delphi、FPC)のような現代のPascalや、現代のC実装では、C++スタイルのコメントが許可されています。{ block comments }(* block comments *)// line comments
識別子とキーワード
C と Pascal は、大文字と小文字の解釈が異なります。C は大文字と小文字を区別しますが、Pascal は区別しません。したがってMyLabel、 と はmylabelC では異なる名前ですが、Pascal では同一です。どちらの言語でも、識別子は文字と数字で構成され、最初の文字は数字であってはならないという規則があります。C では、アンダースコアは文字としてカウントされるため、_abc も有効な名前です。先頭にアンダースコアがある名前は、C の特別なシステム識別子を区別するためによく使用されます。
C と Pascal はどちらもキーワード(言語で使用するために予約されている単語) を使用します。例としては、if、while、const、for、gotoなどが挙げられますが、これらは両方の言語に共通するキーワードです。C では、基本的な組み込み型名もキーワード (例: int、char ) またはキーワードの組み合わせ (例: unsigned char ) ですが、Pascal では組み込み型名は事前定義された通常の識別子です。
定義、宣言、ブロック
Pascal では、サブルーチン定義はキーワードprocedure (値が返されない) またはfunction (値が返される) と、typeによる型定義で始まります。C では、すべてのサブルーチンに定義 (プロシージャ) があり、型定義にはキーワード が使用されます。どちらの言語も、複合型の定義にキーワードと句読点を組み合わせて使用します。たとえば、配列は Pascal ではキーワード array で定義され、C では句読点で定義されます。一方、列挙型はCではキーワードで定義されますが、Pascal では句読点で定義されます。
functionvoid functiontypedefenum
Pascal サブルーチンでは、beginとend がローカル宣言に続く文のブロックを区切りますが、C 関数では "{" と "}" を使用して、オプションで宣言に続く文のブロックを区切ります。C (C99 より前) では、宣言は特定のブロック内の文の前に出現する必要があると厳密に定義されていますが、ブロック内にブロックが出現することは許可されています。これは、この問題を回避する方法です。サブルーチン本体の構文により、Pascal では宣言が文の前に出現することを強制します。また、Pascal では、変数宣言だけでなく、型と関数の定義を、任意の深さの関数定義によってカプセル化できます。
実装
両方の言語の文法は同様のサイズです。実装の観点から見ると、2 つの言語の主な違いは、 C を解析するには型のシンボル テーブルにアクセスする必要があるのに対し、Pascal にはそのような構成要素が 1 つだけ (代入) しかないことです。たとえば、C フラグメントは、を、その型が へのポインタであるオブジェクトとしてX * Y;宣言したり、と を乗算するステートメント式にすることができます。対照的に、対応する Pascal フラグメントと は本質的に明確であり、正しい解析にはシンボル テーブルは必要ありません。
YXXYvar Y : ^X;Z := X * Y;
単純な型
整数
Pascal では、すべての変数と関数の宣言でその型を明示的に指定する必要があります。従来の C では、ほとんどのコンテキストで型名を省略することができ、デフォルトの型int(Pascal では に相当integer) が暗黙的に想定されます (ただし、このようなデフォルトは C では悪い習慣とみなされ、警告によってフラグが付けられることがよくあります)。
C では、、、、などの修飾子を使用して、整数のさまざまなサイズと符号付きモードおよび符号なしモードに対応します。結果の整数型の正確な意味はマシンに依存しますが、はより短くなく、 はより短くないことが保証されます。ただし、C 標準では、 の最小サイズが少なくとも指定されており、 は1バイトであり、 は少なくとも 2 バイトであることが保証されます。
longshortsignedunsignedlong intintintshort intcharint
サブレンジ
Pascal では、整数のサブ範囲を宣言することによって同様の目的が達成されます(コンパイラは、宣言された変数に対してより小さな量のストレージを割り当てることを選択する場合があります)。
a = 1 .. 100 ; b = - 20 .. 20 ; c = 0 .. 100000 ;と入力します。
このサブ範囲機能は C ではサポートされていません。
C と Pascal の大きな、しかし微妙な違いは、整数演算の促進方法です。Pascal では、中間結果が整数に収まらない場合でも、演算の結果はすべての整数/サブ範囲型に対して定義されます。結果は、代入の左側の整数/サブ範囲に収まらない場合にのみ未定義になります。これは、整数型の範囲に対する人為的な制限を意味する場合があり、中間結果を処理するために実行速度が遅くなる可能性があります。ただし、コンパイラは制限されたサブ範囲を利用して、より効率的なコードを生成する場合があります。
C では、オペランドはまず必要な結果のサイズに拡張する必要があります。拡張されたオペランドの範囲に収まらない中間結果は未定義です。必要な結果の範囲がオペランドの範囲より大きい場合、優れた最適化コンパイラであっても、通常は遅くて非効率的なコードが生成されます。ただし、C コンパイラは範囲外の中間結果を処理する必要はなく、また期待もされていません。すべての中間結果がオペランドの範囲に収まるようにするのはプログラマの責任です。
C の Pre-Standard 実装や Small-C などでは、整数型とポインタ型を比較的自由に混在させることができました。
文字の種類
C では、文字型はcharで、 、 より長くない整数の一種ですshort int。したがって、 などの式は完全に合法であり、や'x'+1などの宣言も合法です。
int i='i';char c=74;
このchar(1バイト)の整数の性質は、次のような宣言によって明確に示されています。
unsigned char uc = 255 ; /* 共通制限 */ signed char sc = -128 ; /* 共通負制限 */
char型を と見なすかsigned、またはデフォルトで と見なすかはunsigned、実装次第です。
Pascal では、文字と整数は異なる型です。組み込みコンパイラ関数とを使用してord()、chr()単一の文字を使用中の文字セットの対応する整数値に型変換できます。また、その逆も可能です。たとえば、ASCII 文字セットを使用するシステムではord('1') = 49、と はchr(9)タブ文字です。
ブール型
Pascal では、boolean は列挙型です。booleanの可能な値はfalseとtrueで、順序値は false = 0、 true = 1 です。integerへの変換にはordが使用されます 。
i := ord ( b ) ;
整数をブール値に変換する標準的な関数はありませんが、実際には変換は簡単です。
b := i <> 0 ;
C にはブール型はありません。C は、常に 0 または 1 の結果を返すという意味でブール型と見なすことができる 2 値関係演算子 (<、>、==、!=、<=、>=) を使用します。すべてのテスト (&&、||、?:、 if、whileなど) はゼロチェックによって実行されるため、false はゼロで表され、true はboolその他の値で表されます。これは、で定義されている数値データ型で確認できますstdbool.h。
ビット演算
C では、ビット 演算子を使用してブール演算を実行できます。オペランドが複数のビットを使用して値を表す場合、セマンティクスが異なるため注意が必要です。
Pascal には、ビット単位のデータを処理するための、より抽象的で高レベルの別の方法であるセットがあります。 セットを使用すると、プログラマーは、直接ビット単位の演算子 (最新の Pascal でも使用可能) を使用する代わりに、ビット単位のデータ値を設定、クリア、交差、結合できます。 例:
パスカル:
Status := Status + [ StickyFlag ] ; Status := Status - [ StickyFlag ] ; if ( StickyFlag in Status ) then ...
(* または、ビット演算子を使用します: *)
Status := StatusまたはStickyFlag ; Status := StatusかつStickyFlagではありません; StickyFlagかつStatus = StickyFlagの場合...
C:
Status |= StickyFlag ; Status &= ~ StickyFlag ; if ( Status & StickyFlag ) { ...
セットがビットを使用して実装されている場合、整数に対するビット操作とセットに対する操作は類似していると見なすことができますが、整数とセット間の非標準変換が可能でない限り、それらの使用法の間に直接的な類似点はありません。
実装に関する注意
式の評価中、および両方の言語では、ブール値は、マシン、コンパイラ、および状況に応じて、単一ビット、単一バイト、完全なマシン ワード、生成されたコード内の位置、またはステータス レジスタ内の条件コードとして内部的に格納される場合があります。通常、これらの要素は、コンパイルされた言語よりも重要です。
浮動小数点型
C の浮動小数点型のモデルはPascal ほど厳密ではありません。C では、整数は暗黙的に浮動小数点数に変換され、その逆も可能です (ただし、精度が失われる可能性がある場合は警告が表示されます)。Pascal では、整数は暗黙的に に変換されますがreal、 から への変換(情報が失われる可能性がある) は、それぞれ小数点を切り捨てる関数と四捨五入する関数を介して明示的realに行う必要があります。
integertrunc()round()
列挙型
C と Pascal の両方に列挙型が含まれています。Pascal の例:
type
color = ( red , green , blue ) ; var a : color ;
AC の例:
列挙型カラー{赤、緑、青};列挙型カラーa ;
ただし、2 つの言語の型の動作は非常に異なります。Pascal では、列挙型は序数であり、 を使用して解析されord()、関数succ()とは構造体pred()とは区別されますarray。C では、列挙型は実際には として実装されarrays、 は0、は 1、は 2redと同義になり、この範囲外の値を変数 に割り当てることを妨げるものは何もありません。さらに、 のような演算はPascal では固く禁じられています。代わりに を使用します。C では、列挙型と int を自由に変換できますが、Pascal では、列挙型から整数に変換するには 関数を使用する必要があります。反対に、値の戻り値にはのような型キャスト演算を使用する必要があります。
greenblueaa = a + 1;a := succ(a);ord()a := color(1)green
構造化型
配列型
C と Pascal はどちらも、他の配列を含む他の複雑な型の配列を許可します。ただし、言語間の類似点はそれだけです。C 配列は、基本型と要素の数によって単純に定義されます。
int a [サイズ];
常に 0 から SIZE−1 (つまり SIZE を法とする) までインデックスが付けられます。
Pascalでは、インデックスの範囲は、(上記の単純型で紹介したように)サブ範囲によって指定されることが多い。
var a :整数の配列[ 0 .. 9 ] ;
0..9 でインデックス付けされます (この場合、C と同じです)。配列のインデックスは任意の順序データ型にすることができますが、範囲だけではありません。
type
TColor = ( red , green , blue ) ; (* 列挙 *) RGB = 0 .. 255の配列[ TColor ] ;
var picture : RGBの配列[ 1..640,1..480 ]
varパレット:バイトの配列[ byte , 0 .. 2 ]
n (>1) 文字で構成される文字列は、範囲 1..n のパックされた配列として定義されます。
配列とポインタ
C 式では、配列を表す識別子は配列の最初の要素への定数ポインターとして扱われるため、宣言int a[10]とint *p;割り当てp = aが有効になり、p と a が同じ配列を指すようになります。ただし、識別子は定数aアドレスを表すため、は有効ではありません。
a = p
C の配列は固定されていますが、配列へのポインタは交換可能です。この柔軟性により、C では同じコードを使用して任意の長さの配列を操作できます。また、言語にチェックが組み込まれていないため、割り当てられた配列の外部に書き込まないという責任がプログラマーに課せられます。
Pascal では、配列はポインタとは異なる型です。これにより、コンパイラの観点から配列の境界チェックが可能になります。実質的にすべての Pascal コンパイラは、コンパイルオプションとして範囲チェックをサポートしています。実行時に長さが変わる配列を持ち、言語制御下でチェックできる機能は、しばしば「動的配列」と呼ばれます。Pascal では、各配列型の要素数はコンパイル時に決定され、プログラムの実行中に変更することはできません。したがって、長さがプログラム データに何らかの形で依存する配列を定義することはできません。(注: 1986 年以降、業界標準であった Turbo Pascal 3 では、GetMem()ISO 標準ではないものの、日常的な Pascal で動的配列が許可されています)
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 ++ )は( wordlist [ i ] ) をputs します。for ( i = listSize -1 ; i >= 0 ; i -- )は( wordlist [ i ] )を puts します。
同様に、Delphi や Free Pascal などの最新の Pascal にも同様の機能があります。初期化された配列は次のように実装できます。
var
wordlist :文字列の配列= [ '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 には、配列の初期化 (文字列の場合を除く) も、コンパイル時に任意の配列サイズを決定する手段もありません。自動サイズ調整なしで、上記の例をオリジナルの Pascal で実装する方法の 1 つは、次のとおりです。
定数
minlist = 1 ; maxlist = 5 ; maxword = 7 ;
タイプ
listrange = minlist .. maxlist ; wordrange = 1 .. maxword ; word =レコード内容: charのパックされた配列[ wordrange ] ;長さ: wordrange end ; wordlist = wordの配列[ listrange ] ;変数i :整数; words : wordlist ;
手順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 .
文字列
どちらの言語でも、文字列は文字のプリミティブ配列です。
Pascal では、長さ n の文字列リテラルは型と互換性がありますpacked array [1..n] of char。C では、文字列は通常 型を持ちますchar[n]。
Pascal は可変長配列をサポートしていないため、文字列操作を実行するルーチンのセットは特定の文字列サイズに依存します。現在標準化されている Pascal の「適合配列パラメータ」拡張機能により、この問題は大幅に解決され、Pascal の実装の多く、あるいはほとんどが言語固有の文字列をサポートしています。
C 文字列リテラルはヌルで終了します。つまり、文字列の終わりを示す末尾のヌル文字が付きます。
const char * p ; p = "スペインの雨" ; /* null で終了 */
配列に格納されている文字列変数については、null 終端を手動で維持する必要があります (多くの場合、これはライブラリ ルーチンによって部分的に処理されます)。
C には組み込みの文字列または配列の割り当てがないため、文字列は p に転送されず、p はメモリ内の定数文字列を指すようになります。
Pascal では、C とは異なり、文字列の最初の文字要素はインデックス 0 ではなく 1 にあります (長さプレフィックスが付きます)。これは、Pascal が文字列の長さを文字配列の 0 番目の要素に格納するためです。この違いが十分に理解されていないと、両方の言語で生成されたオブジェクト コードを移植したりインターフェイスしようとしたりするときにエラーが発生する可能性があります。
FreeBSD開発者のPoul-Henning Kamp は、 ACM Queueに寄稿し、長さプレフィックス付き文字列に対するヌル終端文字列の勝利を「これまでで最も高価な 1 バイトの間違い」と呼んだ。[1]
レコードの種類
C と Pascal はどちらも「レコード」型を宣言できます。C では、これらは「構造体」と呼ばれます。
構造体a { int b ; char c ; };
タイプa =レコードb :整数; c :文字;終了;
Pascal では、 name_of_record . name_of_fieldと書く代わりに、 " with name_of_record do"という文を使用して、ローカル変数などのそのレコードのフィールドを直接使用できます。次に例を示します。
type r = record s : string ; c : char ; end ; var r1 : r ; begin with r1 do begin s := 'foo' ; c := 'b' ; end ;
C には と同等の機能はありません。
C では、フィールドの正確なビット長を指定できます。
構造体a { unsigned int b : 3 ; unsigned int c : 1 ; };
使用されるストレージの量は、ターゲット システムの特性 (単語の配置など) によって異なります。
この機能は、キーワードpackedと組み合わせてサブレンジ構造 (3 ビットで 0 から 7 の範囲を表す) を使用することで Pascal で利用できます。
タイプa =パックされたレコードb : 0..7 ; c : 0..1 ;終了;
C と Pascal はどちらも、互いに重複する異なるフィールドを含むことができるレコードをサポートしています。
ユニオンa { int a ; float b ; };
タイプa =レコードケースのブール値false : ( a :整数) ; true : ( b :実数)終了;
どちらの言語プロセッサも、これらのレコードに、ユニオン/レコード内の最大の型を格納するために必要なだけのスペースを自由に割り当てることができます。Pascal では、このような構造はバリアント レコードと呼ばれますが、 Free Pascal で定義されている Variantデータ型と間違えないようにしてください。
C と Pascal の最大の違いは、Pascal では、バリアント レコードの有効なコンポーネントがアクセスされているかどうかを言語プロセッサが判断するための「タグフィールド」の明示的な使用をサポートしていることです。
type a = record case q : boolean of false : ( a :整数) ; true : ( b :実数) end ;
この場合、レコードの適切な部分にアクセスするには、タグ フィールド q を適切な状態に設定する必要があります。
ポインタ
C では、オブジェクトや関数を含むほとんどのプログラム エンティティを指すようにポインタを作成できます。
int a ; int * b ; int ( * compare )( int c , int d ); int MyCompare ( int c , int d ); b = &a a ;比較= & MyCompare ;
C では、配列とポインターはほぼ同等であるため、以下は同じです。
a = b [ 5 ]; a = * ( b + 5 ); a = * ( 5 + b ); a = 5 [ b ];
したがって、ポインタは、配列にアクセスするための別の方法として C でよく使用されます。
動的データを作成するには、ライブラリ関数malloc()とをfree()使用して、動的なデータ ブロックを取得および解放します。したがって、動的メモリ割り当ては言語プロセッサに組み込まれていません。これは、C がオペレーティング システム カーネルまたは組み込みターゲットで使用されている場合に特に役立ちます。これらのものはプラットフォーム (アーキテクチャだけでなく) に非常に依存しており、使用されるプラットフォーム (またはオペレーティング システム) ごとに C コンパイラを変更する必要があるためです。
^Pascal には、C の の代わりに参照演算子を介して、C と同じ種類のポインタがあります。*各ポインタは単一の動的データ項目にバインドされ、割り当てによってのみ移動できます。
a = ^整数と入力します。
var b , c : a ;
新しい( b ) ;
c := b ;
Pascal のポインタは型安全です。つまり、あるデータ型へのポインタは、同じデータ型のポインタにのみ割り当てることができます。また、ポインタを非ポインタ変数に割り当てることはできません。ポインタ演算 (特にエンディアンの問題やプラットフォームに依存しない型のサイズと組み合わせると、C でのプログラミング エラーの一般的な原因) は、Pascal では許可されていません。これらの制限により、C と比較して Pascal でのポインタ関連のエラーの可能性は減りますが、Pascal での無効なポインタ参照を完全に防止することはできません。たとえば、ポインタが初期化される前または破棄された後に参照されると、ランタイム エラーが発生します。
表現
優先順位
式の評価に関しては言語によって大きく異なりますが、全体的には比較可能です。
パスカル
- 論理否定:
not - 乗法:
* / div mod and - 添加剤:
+ - or - リレーショナル:
= <> < > <= >= in
C
- 単項接尾辞:
[] () . -> ++ -- - 単項接頭辞:
& * + - ! ~ ++ -- (type) sizeof - 乗法:
* / % - 添加剤:
+ - - シフト:
<< >> - リレーショナル:
< > <= >= - 平等:
== != - ビットごとの論理積:
& - ビット排他的論理和:
^ - ビット演算:
| - 論理積:
&& - 論理和:
|| - 条件:
? : - 割り当て:
= += -= *= /= %= <<= >>= &= ^= |= - カンマ演算子:
,
タイピング
Pascal では、ほとんどの演算子が複数の目的に使用されます。たとえば、マイナス記号は否定、減算、または集合の差 (型と構文コンテキストの両方によって異なります) に使用でき、>=演算子は数値、文字列、または集合の比較に使用できます。C では、専用の演算子記号がより多く使用されます。
割り当てと等価性のテスト
2 つの言語は、代入に異なる演算子を使用します。 Pascal は、ALGOLと同様に、等価性テストに数学的な等価演算子を使用し=、代入に記号を使用しますが、 C は、 B:=と同様に、代入に数学的な等価演算子を使用します。 C (および B) では、等価性テストに
FORTRANの記号が選択されました。==
C では、経験不足または単純な入力ミスが原因で、 などの条件文に代入式を誤って入れてしまうことがよくあります。 代入式の値は 10 で、これは非ゼロであり、C では「真」とみなされるため、中括弧内のコードは常に実行されます。これは、C (および ALGOL) が 形式での多重代入を許可しているが、これが Pascal ではサポートされていないことが一因です。 また、の値が になったことにも注意してください。これは、次のコードに影響する可能性があります。最近の C コンパイラは、このようなケースを検出してユーザーに警告し、 のようなあいまいさの少ない構文を使用するように求めます。
if (a = 10) { ... }a = 10a = b = c = 10;a10if ((a=10) != 0 ) { ... }
代入は式ではなく値も持たないため、この種の間違いは Pascal では起こりません。間違った演算子を使用すると明確なコンパイル エラーが発生し、シンボルを:=等価性テストと間違える可能性も低くなります。
形式の ALGOL の条件式には、C には同等のもの ( CPL の三項演算子) がありますが、Pascal には同等のものがなく、 が使用される点に注意してください。
z := if a > b then a else b;if a > b then z:=a; else z:=b;
実装上の問題
Niklaus Wirth がPascal を設計したとき、優先順位のレベル数を制限したい (つまり、解析ルーチンの数を少なくしたい) という要望がありました。そのため、OR 演算子と排他的 OR 演算子は Addop のように扱われ、数式のレベルで処理されます。同様に、AND は Mulop のように扱われ、Term で処理されます。優先順位は次のとおりです。
両方の種類の演算子に適用される構文規則は1つだけであることに注目してください。この文法によれば、次のような式は
x + (y かつ z ではない) / 3
これらは完全に合法です。そして実際、パーサーに関する限り、これらは合法です。Pascal では算術変数とブール変数の混在は許可されておらず、このようなことは構文レベルではなく、コードを生成するときに意味レベルで検出されます。
C の作者たちは正反対のアプローチを採用しました。つまり、演算子を異なるものとして扱い、実際、C には 15 レベル以上あります。これは、C にも '='、'+=' などの演算子と、その類似の '<<'、'>>'、'++'、'--' などがあるためです。C では算術演算子とブール演算子は別々に扱われますが、変数は別々に扱われません。つまり、ブール テストは任意の整数値に対して実行できます。
論理接続詞
Pascal では、特定の評価順序 (関数呼び出しの副作用による可能性あり) に依存するブール式は、多かれ少なかれエラーと見なされます。Pascal コンパイラは、任意の順序を使用する自由があり、結果が部分的な評価で決定できる場合でも、常に式全体を評価する必要があります。(注: Turbo Pascal 3 (1986) 以降、短絡ブール評価は、ISO 標準では利用できませんが、日常的な Pascal で利用できます)。
C では、ブール&&評価順序への依存は完全に合法であり、 and演算子を、、、カンマ演算子など||の演算子と組み合わせて体系的に使用することがよくあります。 したがって、 and演算子は、論理演算子と条件文の組み合わせとして機能します。
+++=&&||
短絡式評価は、一般的に「評価問題」のため、C にとって利点であると考えられてきました。
var i :整数; a : charのパックされた配列[ 1 .. 10 ] ; ... i := 1 ; while ( i <= 10 ) and ( a [ i ] <> 'x' ) do i := i + 1 ; ...
この一見単純な検索は、配列アクセス a[i] が i が 11 の場合無効になるため、Pascal では問題があります。この問題を回避する方法は複数あります。次の例では、対象の文字が見つかったかどうかを示すブール変数を導入しています。
const
strlen = 10 ; var i :整数; a : charのパックされた配列[ 1 .. strlen ] ; found :ブール値; ... i := 1 ; found := false ; while not found and ( i <= strlen ) do if ( a [ i ] = 'x' ) then found := true else i := i + 1 ; ...
あるいは、配列の終わりのテストを配列アクセスから分離し、ターゲットが見つかった場合に goto ステートメントで検索を終了できます。
ラベル99 ; const strlen = 10 ; var i :整数; a : charのパックされた配列[ 1 .. strlen ] ; ... i := 1 ; a [ i ] = 'x'の場合は99に進み、i := i + 1 をi > strlenになるまで繰り返します; 99 : ...
制御構造
制御構造を構築するためのステートメントは、ほぼ類似しており、比較的似ています (少なくとも最初の 3 つは)。
Pascal の元の形式では、defaultに相当するものはありませんでしたが、同等の句は一般的な拡張機能です。それ以外の場合、Pascal プログラマーは、 if expr not in [A..B] then default-caseなどの式を使用して case 文を保護する必要がありました。
else
C には、いわゆる早期終了ステートメントbreakとcontinue があり、一部の Pascal にもそれらがあります。
C と Pascal の両方にgotoステートメントがあります。ただし、Pascal にはネストされたプロシージャ/関数があるため、内部のプロシージャまたは関数からそれを含むプロシージャまたは関数にジャンプできます。これは、エラー回復を実装するためによく使用されていました。C には、ANSI C setjmpとlongjmp を介してこの機能があります。これは同等ですが、ジャンプ アドレスやスタック フレームなどのプログラム固有の情報をプログラマがアクセス可能な構造に格納するため、安全性は低くなります。
機能と手順
値を返す Pascal ルーチンは関数と呼ばれ、値を返さないルーチンはプロシージャと呼ばれます。C のすべてのルーチンは関数と呼ばれ、値を返さない C 関数は戻り値の型voidで宣言されます。
Pascal プロシージャは C の "void" 関数と同等であると見なされ、Pascal 関数は値を返す C 関数と同等です。
C での次の 2 つの宣言:
int f ( int x 、int y ); void k ( int q );
これらは Pascal の次の宣言と同等です。
関数f ( x , y :整数) :整数;手順k ( q :整数) ;
Pascal には、値渡しと参照渡し (VAR) の 2 種類のパラメータがあります。どちらの場合も、呼び出し時に変数名が使用されます (アドレス演算子は不要)。
function f ( z : integer ; var k : integer ) : integer ; // 関数は 2 つの整数を受け入れます。1 つは値、もう 1 つは参照です。Begin z := 1 ; // 外部変数 u は変更されませんが、関数のスコープ内でローカル値が変更されます。k := 1 ; // 外部変数 t は参照によって渡されたため変更されます。// ここまでは、 z が存在し、1 に等しいです。End ;
x := f ( u , t ) ; // 変数 u と t が呼び出しに渡されます: u の値と t への参照
C ではすべてのパラメータは値渡しされますが、参照渡しはポインタを使用してシミュレートできます。次のセグメントは、上記の Pascal セグメントに似ています。
int f ( int z , int * k ) { //関数は、int (値渡し) と int へのポインタ (値渡し) をパラメータとして受け入れます。z = 1 ; // Pascal と同様、ローカル値は変更されますが、外側の u は変更されません。* k = 1 ; // k によって参照される変数 (例: t) は変更されます。// ここまでは、z が存在し、1 に等しいです。 }
x = f ( u , & t ); // uの値と変数tのアドレス(の値)が呼び出しに渡されます
C と Pascal の最も重要な違いの 1 つは、サブルーチン呼び出し中にスタック上のパラメータを処理する方法です。これは呼び出し規約と呼ばれます 。PASCAL スタイルのパラメータは、左から右の順序でスタックにプッシュされます。C の STDCALL 呼び出し規約では、パラメータは右から左の順序でスタックにプッシュされます。
Pascal スタイルのプロシージャ呼び出しは次のように行われます。
- 呼び出し元は左から右の順序でパラメータをスタックにプッシュします(__cdeclの反対)
- 関数を呼び出す
- スタックは呼び出し側によってクリーンアップされる
; パスカルスタイルの呼び出しの例。
; 注意: __stdcall は引数を逆の順序でプッシュします。push arg1 push arg2 push arg3 call function ; 戻り時にスタックのクリーンアップは行われません。呼び出し先がそれを行いました。
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
WriteLn()Pascal と C にも、やなどの可変長 I/O 関数がいくつかありますprintf()。
現代の Pascal では、関数のパラメータ数を可変にすることができます。
手順writeLines ( const引数: constの配列) ; // 次のように解析されます : for argument in arguments do
また、varargs C 関数とのインターフェースも可能になります。
関数PrintF1 ( fmt : pchar ) ; cdecl ; varargs ;外部'c'名'printf' ;
Pascal では、プロシージャと関数をネストできます。これは、プロシージャのグループにローカルだがグローバルではない変数を許可する場合に便利です。C にはこの機能がなく、変数または関数のローカライズは、変数または関数がstatic として宣言されているコンパイル モジュールに対してのみ実行できます。
C および Pascal では、関数ポインタを介して関数を間接的に呼び出すことができます。次の例では、ステートメントは(*cmpar)(s1, s2)と同等ですstrcmp(s1, s2)。
#include <文字列.h>
int ( * cmpar )( const char * a , const char * b ); const char * s1 = "hello" ; const char * s2 = "world" ;
cmpar = & strcmp ; b = ( * cmpar )( s1 , s2 );
Pascal では、関数とプロシージャを関数またはプロシージャにパラメータとして渡すことができます。
手順ShowHex ( i :整数) ; ...終了;
手順ShowInt ( i :整数) ; ...終了;
プロシージャデモ(プロシージャショー( i :整数)) ; var j :整数; begin Show ( j ) end ;
...
デモ( ShowHex ) ;デモ( ShowInt ) ; ...
プリプロセッサ
初期の C には定数宣言も型宣言もありませんでした。また、C 言語はもともと「プリプロセッサ」を必要とする言語として定義されていました。プリプロセッサとは、メモリ使用量を抑えるために定数、インクルード、マクロ定義を処理する別のプログラムとパスです。その後、ANSI C では定数と型定義の機能が追加され、プリプロセッサも言語の一部となり、現在のような構文になりました。
Pascal の定数と型定義は組み込まれており、プリプロセッサは必要ありません。Pascal でもプリプロセッサを使用しているプログラマーがいました (C で使用されるものと同じものを使用することもあります) が、C ほど一般的ではありません。Pascal の「欠点」としてよく指摘されますが、技術的には C にはプログラムのモジュール性もマクロも組み込まれていません。C には単純な低レベルの個別コンパイル機能がありますが (従来はアセンブリ言語で使用されるのと同じ汎用リンカーを使用)、Pascal にはこの機能がありません。
型エスケープ
C では、プログラマーはポインターを指定して任意のオブジェクトのバイトレベルの表現を検査できますchar。
int a ; char * p = ( char * )( &a a ); char c = * p ; // 最初のバイト
区別のないバリアントレコードを使用して、Pascal で同様のことを行うことが可能かもしれません。
var a :整数; b :実数; a2c :レコードケースのブール値false : ( a :整数) ; true : ( b :実数) ; end ; end ; begin a2c.b : = b ; a : = a2c.a ; end ;
ほとんどの Pascal コンパイラとインタープリタではキャストが可能ですが、上記のコードでも、a2c.a と a2c.b は同じアドレス空間を共有することが Pascal の標準化によって要求されていません。Pascal の設計者である Niklaus Wirth は、このアプローチを使用して型エスケープを試みることの問題点について次のように書いています。
「Pascal の実装者のほとんどは、このチェックはコストがかかりすぎて、コードが大きくなり、プログラムの効率が低下すると判断しました。その結果、バリアント レコードは、トリックが大好きなすべてのプログラマーが型システムを破るためのお気に入りの機能となり、通常は落とし穴や災難に変わります。」
現在、Java、C#、Wirth 自身のOberonなど、いくつかの言語では、このような型エスケープが明確に除外されています。
ファイル
C では、ファイルは組み込み型として存在せず (システム ヘッダーで定義されます)、すべての I/O はライブラリ呼び出しによって行われます。Pascal では、ファイル処理が言語に組み込まれています。
各言語で I/O を実行するために使用される一般的なステートメントは次のとおりです。
printf ( "合計は: %d \n " , x );
writeln ( '合計は: ' , x ) ;
主な違いは、C では printf 関数の引数を見つけて変換するために解釈される「フォーマット文字列」を使用するのに対し、Pascal では言語プロセッサの制御下でそれを実行することです。解釈が行われないため、Pascal 方式の方が高速であると言えますが、C 方式は拡張性が非常に高いです。
後のPascal実装と拡張
いくつかの人気のあるPascal実装では、事実上すべてのC構造がPascalに組み込まれています。例としては、型キャスト[2]、ローカルまたはグローバルの任意の変数のアドレスを取得できること、特別なプロモーションプロパティを持つさまざまな型の整数などがあります。
ただし、型と型変換に対する C の寛容な姿勢を取り入れると、Pascal は型セキュリティの一部またはすべてを失う可能性があります。たとえば、JavaとC# は、C の型セキュリティの問題の一部に対処するために作成され、無効な参照を作成するために使用できない「管理された」ポインタを備えています。元の形式 ( Niklaus Wirthによって説明) では、Pascal は Java または C# より約 30 年早く、管理されたポインタ言語として適格です。ただし、C と融合した Pascal は、定義上その保護を失います。一般に、基本的なタスクに対するポインタへの依存度が低いため、実際には C よりも安全です。
拡張 Pascal 標準は、Pascal を拡張して、元の標準 Pascal ではサポートされていなかった C の多くの機能を、より型安全な方法でサポートします。たとえば、スキーマ型は (他の用途に加えて) 可変長配列をサポートしながら、配列次元を配列と共に必ず保持するという型の安全性を維持し、動的にサイズが調整される配列でも範囲外のインデックスの自動実行時チェックを可能にします。
参照
注記
さらに読む
- Kathleen Jensen と Niklaus Wirth: PASCAL - ユーザー マニュアルとレポート。 Springer-Verlag、1974、1985、1991、ISBN 3-540-97649-3 https://web.archive.org/web/20050314152247/http://www.cs.inf.ethz.ch/~wirth/books /パスカル/]
- Brian Kernighan、Dennis Ritchie : C プログラミング言語。K&R とも呼ばれる、C のオリジナルの本。
- Niklaus Wirth: PASCAL 37-38 の動的配列に関する注記に対するコメント、ACM SIGPLAN Notices、第 11 巻、第 1 号、1976 年 1 月。
- Niklaus Wirth: Pascal の開発に関する回想。333-342、ACM SIGPLAN Notices、第 28 巻、第 3 号、1993 年 3 月。
- ISO/IEC 9899。欠陥レポートと根拠を伴う公式の C:1999 標準。
- CからPascalへの変換の詳細な分析
- Alan R. Feuer、Narain H. Gehani: プログラミング言語 C と Pascal 73-92 の比較、ACM Computing Surveys、第 14 巻、第 1 号、1982 年 3 月。
- プログラミング言語の比較と評価: Ada、C、Pascal、編者: Alan R. Feuer、Narain Gehani、Prentice Hall、1984年。ISBN 0-13-154840-9
- スコット・マイヤーズ: Effective C++、第 2 版、Addison-Wesley、1998 年、ISBN 0-201-92488-9
- Vincent Hayward: プログラミング言語 Pascal と C 50-60 の比較分析、ACM SIGPLAN Notices、第 21 巻、第 5 号、1986 年 5 月。
- FreePascal コンパイラ Wiki の C ユーザー向け Pascal
