コンピュータプログラミング言語であるCとPascalは、誕生時期、影響、目的において類似点が多い。どちらも初期の段階で独自のコンパイラを設計(およびコンパイル)するために用いられた。Pascalの原型となる定義は1969年に発表され、最初のコンパイラは1970年に登場した。C言語の最初のバージョンは1972年に発表された。
どちらもALGOL言語シリーズの子孫です。ALGOLは、if、while、for、caseなどの単一エントリと単一出口の構成要素でプログラムが構成される構造化プログラミングのためのプログラミング言語サポートを導入しました。PascalはALGOL Wから直接派生していますが、 ALGOL 68といくつかの新しいアイデアを共有しています。C言語は、当初はB、BCPL、CPLを介して、後にALGOL 68(たとえば、およびの場合)およびPascal(たとえば、列挙、およびブール値の場合)を介して、ALGOLとより間接的に関連しています。一部のPascal方言は、Cからの特性も取り入れています。structunionconsttypedef
ここで取り上げる言語は、ニクラウス・ヴィルトが設計し、1982年にISO 7185として標準化されたPascalと、デニス・リッチーが設計し、1989年にC89として標準化されたCです。これらのバージョンはどちらも言語の成熟版であり、また比較的近い時期に開発されたため、比較対象としています。ANSI CおよびC99(後のC規格)の機能、そしてPascalの後期実装(Turbo Pascal、Free Pascalなど)の機能は、堅牢性と機能性の向上をもたらしているにもかかわらず、比較対象には含まれていません(例:PascalとDelphiの比較)。
構文的には、Pascal はCよりも ALGOL に非常によく似ています。C が句読点記号を使用する箇所でも、Pascal ではand、or、 、 が使われますが、modCでは 、 、 、 などが使われます。ただし、(単純な) 宣言に関しては、C は Pascal よりも ALGOL に近く、型名と変数名の構文が保持されています。例えば、C では、関数の外側のブロックだけでなく、任意のブロックの先頭で宣言を受け入れることができます。&&||%
もう一つ、より微妙な違いはセミコロンの役割です。Pascalでは、セミコロンは複合文内の個々の文を区切りますが、C言語では文を終了させます。C言語では、セミコロンは構文的にも文の一部であり(式を文に変換する)、この違いは主に次の2つの状況で顕著に現れます。
else、Cではブロック文を使用しない限りセミコロンは必須です。endまたはの直前の文の後にはuntilセミコロンは不要ですend の前の最後の行に余分なセミコロンを付けることで、形式的に空のステートメントを挿入することができます。
C の最初の版には しかありません/* block comments */。C は後に を追加しました// line comments。
パスカルには、とがあります。{ block comments }(* block comments *)
C と Pascal では、大文字と小文字の解釈が異なります。C は大文字と小文字を区別しますが、Pascal は区別しません。したがって、 C ではMyLabelと はmylabel異なる名前ですが、Pascal では同じ名前です。どちらの言語でも、識別子は文字と数字で構成され、最初の文字は数字であってはならないというルールがあります。C では、アンダースコアは文字としてカウントされるため、_abc も有効な名前です。先頭にアンダースコアが付いた名前は、C で特別なシステム識別子を区別するためによく使用されます。
C言語とPascal言語はどちらもキーワード(言語で使用するために予約されている単語)を使用します。例としては、if、while、const、for、gotoなどがあり、これらは両言語に共通するキーワードです。C言語では、基本的な組み込み型名もキーワード(例:int、char)またはキーワードの組み合わせ(例:unsigned char)ですが、Pascal言語では、組み込み型名はあらかじめ定義された通常の識別子です。
Pascal では、サブルーチンの定義はキーワードprocedure (戻り値なし) またはfunction (戻り値あり) で始まり、型の定義はtypeで始まります。C では、すべてのサブルーチンはfunction定義を持ち (プロシージャはvoid function)、型の定義にはキーワード を使用しますtypedef。どちらの言語も、複雑な型の定義にはキーワードと句読点を併用します。たとえば、配列は Pascal ではキーワードarrayで定義され、C では句読点で定義されますが、列挙型はC ではキーワード で定義されenum、Pascal では句読点で定義されます。
Pascal のサブルーチンでは、beginとend はローカル宣言に続くステートメントのブロックを区切りますが、C の関数では "{" と "}" を使用して、宣言が前に置かれるステートメントのブロックを区切ります 。C (C99 より前)では、特定のブロック内のステートメントの前に宣言が必ず存在しなければならないと厳密に定義されていますが、ブロックの中にブロックが存在することは許容されており、これはこの問題を回避する方法です。サブルーチンの本体の構文により、Pascal では宣言がステートメントの前に存在することが強制されます。また、Pascal では、変数宣言だけでなく、型や関数の定義も、任意の深さまで関数定義でカプセル化できます。
両言語の文法はほぼ同じ大きさです。実装の観点から見ると、両言語の主な違いは、 C言語を解析するには型のシンボルテーブルへのアクセスが必要なのに対し、Pascal言語には代入という1つの構成要素しかない点です。例えば、C言語の断片は、型がへのポインタであるオブジェクトX * Y;を宣言するもの、あるいはとを乗算するステートメント式である可能性があります。これに対し、対応するPascal言語の断片と は本質的に曖昧さがなく、正しい解析にはシンボルテーブルは必要ありません。YXXYvar Y : ^X;Z := X * Y;
Pascalでは、すべての変数と関数の宣言において、型を明示的に指定する必要があります。従来のC言語では、ほとんどの場合、型名を省略することができ、その場合、デフォルトの型int(Pascalでは に相当integer)が暗黙的に想定されます(ただし、このようなデフォルトはC言語では好ましくない慣習とみなされ、警告が表示されることがよくあります)。
C言語では、`n` 、`n`、 `n`などの修飾子を使用することで、整数のさまざまなサイズや符号付き/符号なしモードに対応できます。結果として得られる整数型の正確な意味はマシンに依存しますが、 ` n`は`n`より短くなく、`n`は`n`より短くないことが保証されます。ただし、C標準では、型の最小サイズが少なくとも指定されており、`n`は1バイト、` n`は少なくとも2バイトであることが保証されています。longshortsignedunsignedlong intintintshort intcharint
Pascalでは、整数のサブレンジを宣言することで同様の目的が達成されます(コンパイラは宣言された変数に、より少ない量の記憶領域を割り当てることを選択する場合があります)。
型a = 1 .. 100 ; b = - 20 .. 20 ; c = 0 .. 100000 ;このサブレンジ機能はC言語ではサポートされていません。
C言語とPascal言語の大きな、しかし微妙な違いは、整数演算の扱い方です。Pascalでは、中間結果が整数に収まらない場合でも、演算の結果はすべての整数型/部分範囲型に対して定義されます。結果が未定義となるのは、代入式の左辺の整数型/部分範囲に収まらない場合のみです。これは、整数型の範囲に人為的な制限を課したり、中間結果を処理するために実行速度を低下させたりする可能性があります。しかし、コンパイラは制限された部分範囲を利用して、より効率的なコードを生成する場合があります。
C言語では、オペランドはまず必要な結果のサイズに昇格されなければなりません。昇格されたオペランドの範囲に収まらない中間結果は未定義です。必要な結果の範囲がオペランドの範囲よりも大きい場合、優れた最適化コンパイラであっても、通常は低速で非効率的なコードが生成されます。しかし、Cコンパイラは範囲外の中間結果を処理する必要も期待もされていません。すべての中間結果がオペランドの範囲に収まるようにするのはプログラマの責任です。
C言語の標準規格以前の実装やSmall-Cなどでは、整数型とポインタ型を比較的自由に混在させることができた。
C言語では、文字型は であり、charこれは 、 より長くない一種の整数ですshort int。'x' + 1したがって、 のような式は完全に合法であり、 や のような宣言も同様int i = 'i';ですchar c = 74;。
(1バイトの)この整数的な性質はchar、次のような宣言によって明確に示されています。
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 です。整数への 変換にはordが使用されます。
i := ord ( b ) ;整数をブール値に変換する標準関数はありませんが、実際には変換は簡単です。
b := i <> 0 ;C言語には、 C23以降、ブール型がありますが、それ以前にはありませんでした。C言語では、常にゼロまたは1の結果を返すという意味でブール型とみなせるバイナリ値関係演算子(<、>、==、!=、<=、>=)を使用します。すべてのテスト(&&、||、 ?:、if、whileなど)はゼロチェックで実行されるため、falseはゼロで表され、trueはその他の値で表されます。これは、(C23以前)で定義された数値データ型で確認できます。bool bool<stdbool.h>
C言語では、ビット演算子を用いてブール演算を行うことができます。ただし、オペランドが複数のビットを用いて値を表す場合、意味論が異なるため注意が必要です。
Pascalには、ビット単位のデータを扱うための、より抽象的で高レベルな方法であるセットがあります。セットを使用すると、プログラマは直接ビット演算子を使用する代わりに、ビット単位のデータ値を設定、クリア、交差、および結合できます。例;
パスカル:
Status := Status + [ StickyFlag ] ; Status := Status - [ StickyFlag ] ; if ( StickyFlag in Status ) then ...C:
Status |= StickyFlag ; Status &= ~ StickyFlag ; if ( Status & StickyFlag ) { ...整数に対するビット演算と集合に対する演算は、集合がビットを用いて実装されている場合は類似していると考えることができるが、整数と集合の間で非標準的な変換が可能でない限り、それらの用途に直接的な類似性はない。
式の評価中、どちらの言語においても、ブール値は、マシン、コンパイラ、および状況に応じて、1ビット、1バイト、完全なマシンワード、生成されたコード内の位置、またはステータスレジスタ内の条件コードとして内部的に格納される場合があります。これらの要素は通常、コンパイルされる言語よりも重要です。
C言語は、Pascal言語よりも浮動小数点型のモデルが緩やかです。C言語では、整数は暗黙的に浮動小数点数に変換でき、その逆も可能です(ただし、精度が失われる可能性がある場合は警告が表示される場合があります)。Pascal言語では、整数は暗黙的に浮動小数点数に変換できますrealが、整数から浮動小数点real数への変換(情報が失われる可能性がある)は、それぞれ小数部分を切り捨てたり丸めintegerたりする関数とを使用して明示的に行う必要がありますtrunc()。round()
C言語とPascal言語の両方に列挙型が含まれています。Pascal言語の例を以下に示します。
type color = ( red , green , blue ) ; var a : color ;ACの例:
列挙型Color { RED , GREEN , BLUE };enum Color a = RED ;しかし、2つの言語における型の振る舞いは大きく異なります。Pascalでは、列挙型は順序型でありord()、succ()を使用して解析され、pred()関数と関数は構造体とは異なりますarray。Cでは、列挙型は実際には配列として実装されており、は単に0、1、2REDの同義語となり、この範囲外の値を変数に代入することを妨げるものはありません。さらに、のような操作はPascalでは厳密に禁止されています。代わりにを使用します。Cでは、列挙型は整数と自由に変換できますが、Pascalでは、列挙型から整数に変換するには関数を使用する必要があります。整数を列挙型に変換することはできませんが、列挙型は、そうでなければ整数を使用する可能性のあるステートメントで使用できます。GREENBLUEaa = a + 1;a := succ(a);ord()
for a := red to blue doC言語とPascal言語はどちらも、他の配列を含む、他の複雑な型の配列を許容します。しかし、両言語の類似点はそこで終わります。C言語の配列は、基本型と要素数によって単純に定義されます。
int a [ SIZE ];は常に から0までSIZE - 1(つまり、 を法としてSIZE) インデックスされます。
Pascalでは、インデックスの範囲は、多くの場合、サブレンジによって指定されます(上記の単純な型で紹介したとおり)。
var a :整数型の配列[ 0 .. 9 ] ;0~9でインデックス付けされます(この場合、C言語と同じです)。ただし、配列のインデックスは範囲だけでなく、任意の順序データ型にすることができます。
type TColor = ( red , green , blue ) ; (* 列挙 *) RGB = array [ TColor ] of 0 .. 255 ;var picture :配列[ 1 .. 640 , 1 .. 480 ] of RGBvarパレット:バイトの配列[ 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では、各配列型の要素数はコンパイル時に決定され、プログラムの実行中に変更することはできません。したがって、プログラムデータに何らかの形で長さが依存する配列を定義することはできません。
C言語では、任意の長さの配列を初期化できます。このsizeof演算子を使用すると、Cコード内で静的に初期化された配列のサイズを取得できます。例えば、以下のコードでは、文字列のリストが変更されると、ループの終了インデックスが自動的に調整されます。
#include <stdio.h>static char * wordlist [] = { "print" , "out" , "the" , "text" , "message" }; static int listSize = sizeof ( wordlist ) / sizeof ( wordlist [ 0 ]);for ( int i = 0 ; i < listSize ; i ++ ) { printf ( wordlist [ i ]); }for ( int i = listSize - 1 ; i >= 0 ; i -- ) { printf ( wordlist [ i ]); }Pascalには(文字列の場合を除いて)配列の初期化機能も、コンパイル時に任意の配列サイズを決定する手段もありません。上記の例を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 .どちらの言語においても、文字列は文字の基本的な配列である。
Pascal では、長さ n の文字列リテラルは型 と互換性がありますpacked array [1..n] of char。C では、文字列は一般的に型 を持ちますchar[n]。
Pascalは可変長配列をサポートしていないため、文字列操作を行うルーチンは特定の文字列サイズに依存します。現在では標準化されているPascalの「準拠配列パラメータ」拡張機能によってこの問題は大幅に解決されており、多くの、あるいはほとんどのPascal実装は、言語固有の文字列をサポートしています。
C言語の文字列リテラルはヌル終端されます。つまり、文字列の末尾にはヌル文字が付きます。
// 文字列 s はヌル終端ですconst char * s = "the rain in Spain" ;配列に格納される文字列変数については、ヌル終端を手動で維持する必要があります(これは多くの場合、ライブラリルーチンによって部分的に処理されます)。
C言語には文字列や配列への代入機能が組み込まれていないため、文字列がpに転送されるのではなく、pがメモリ内の定数文字列を指すように設定されている。
Pascalでは、C言語とは異なり、文字列の最初の文字要素はインデックス0ではなく1にあります(そのため、長さが接頭辞として付きます)。これは、Pascalでは文字列の長さが文字配列の0番目の要素に格納されるためです。この違いを十分に理解していないと、両方の言語で生成されたオブジェクトコードを移植したり、インターフェースを試みたりする際にエラーが発生する可能性があります。
FreeBSD開発者のPoul-Henning Kamp は、 ACM Queueに寄稿した記事の中で、後にヌル終端文字列が長さ接頭辞付き文字列に勝利したことを「史上最も高価な 1 バイトのミス」と呼んだ。[ 1 ]
C言語とPascal言語はどちらも「レコード」型を宣言できます。C言語では、これらは「構造体」と呼ばれます。
struct A { int b ; char c ; };type a = record b : integer ; c : char ; end ;Pascalでは、「 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 end ;C言語には、これに相当する機能はありません。
C言語では、フィールドの正確なビット長を指定できます。
struct A { unsigned int b : 3 ; unsigned int c : 1 ; };使用されるストレージ容量は、対象システムの特性(例えば、単語アライメント)によって異なります。
この機能は、Pascal ではサブレンジ構文 (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 ;どちらの言語プロセッサも、共用体/レコード内の最大の型を格納するために必要なだけの領域をこれらのレコードに割り当てることができます。Pascalでは、このような構造はバリアントレコードと呼ばれ、 Free Pascalで定義されているVariantデータ型と混同しないように注意が必要です。
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 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 では許可されていません。これらの制限により、Pascal では C 言語に比べてポインタ関連のエラーが発生する可能性は低くなりますが、Pascal で無効なポインタ参照を完全に防ぐことはできません。たとえば、ポインタが初期化される前、または破棄された後に参照されると、実行時エラーが発生します。
表現の評価方法に関しては言語によって大きな違いがあるが、全体的には比較可能である。
パスカル
not* / div mod and+ - or= <> < > <= >= inC
[] () . -> ++ --& * + - ! ~ ++ -- (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){...}
パスカルでは、代入は式ではなく値を持たないため、このような間違いは起こりません。間違った演算子を使用すると明確なコンパイルエラーが発生し、また、この:=記号を等価性テストと間違える可能性も低くなります。
ALGOL の条件式は、 C 言語では ( CPL の三項演算子)と同等の形式を持つが、Pascal 言語では同等の形式を持たないことが注目される。z:=ifa>bthenaelseb;ifa>bthenz:=aelsez:=b;
Niklaus WirthがPascalを設計したとき、優先順位のレベル数を制限すること(結局のところ、構文解析ルーチンの数を減らすこと)が目的だった。そのため、OR演算子と排他的OR演算子はAddopのように扱われ、数式のレベルで処理される。同様に、AND演算子はMulopのように扱われ、Termで処理される。優先順位のレベルは
構文規則は1セットしかなく、両方の種類の演算子に適用されることに注意してください。この文法によれば、次のような式は
x + (y AND NOT z) / 3
これらは完全に合法です。実際、構文解析器の観点からは合法です。Pascalでは算術変数とブール変数の混在は認められておらず、このような問題は構文レベルではなく、コード生成時の意味レベルで検出されます。
C言語の開発者たちは、全く逆のアプローチを取りました。演算子を別々に扱い、実際、C言語には15ものレベルが存在します。これは、C言語にも「=」、「+=」、そして「<<」、「>>」、「++」、「--」などの演算子があるためです。C言語では算術演算子とブール演算子は別々に扱われますが、変数は別々に扱われません。ブールテストは任意の整数値に対して実行できます。
Pascalでは、特定の評価順序(関数呼び出しの副作用などを介して)に依存するブール式は、ほぼエラーとみなされます。Pascalコンパイラは、任意の評価順序を自由に選択でき、部分評価で結果が判明する場合でも、常に式全体を評価しなければなりません。
C言語では、ブール値の評価順序に依存することは完全に合法であり、&&and演算子と、、、コンマ演算子など||の演算子を組み合わせて体系的に使用されることがよくあります。したがって、 and演算子は論理演算子と条件文の組み合わせとして機能します。+++=&&||
短絡評価は、一般的に「評価問題」のためにC言語の利点と考えられてきた。
var i : integer ; a : packed array [ 1 .. 10 ] of char ; ... i := 1 ; while ( i <= 10 ) and ( a [ i ] <> 'x' ) do i := i + 1 ; ...一見単純なこの検索は、Pascalではiが11の場合に配列a[i]へのアクセスが無効になるため問題があります。この問題を回避する方法は複数あります。次の例では、目的の文字が見つかったかどうかを示すブール変数を導入しています。
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 ; ...以下では、代わりに繰り返し文を使用しています。
const strlen = 10 ; var i : integer ; a : packed array [ 1..strlen ] of char ; found : boolean ; ... i : = 1 ; repeat found := a [ i ] = ' x' ; if not found then inc ( i ) ; until found or ( i > strlen ) ; ...建築規制構造物に関する規定は類似しているが、同等ではない。
Pascalのfor文から正常に抜け出した場合、制御変数であるidの最終値は未定義のままになります。
Pascalには`default`に相当する機能はありません。そのため、Pascalプログラマーは` if expr not in [A..B] then default-case`のような式でcase文をガードする必要があります。こうすることで、実行されるcase文は最大でも1つだけになります。
C言語には、いわゆる早期終了ステートメントであるbreakとcontinueがあります。これらは必須ではなく、省略すると複数のcase文が実行される可能性があります。
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の関数は式の中でしか使用できませんが、値を返すC関数は「void」関数と同じように呼び出すことができ、その戻り値は無視されます。
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型(値渡し)とint型へのポインタ(こちらも値渡し)を引数として受け取りますint f ( int z , int * k ) { z = 1 ; // Pascalと同様、ローカル変数の値は変更されますが、外部変数uは変更されません* k = 1 ; // kによって参照される変数(例:t)は変更されます// ここまでは、zは存在し、1に等しい}x = f ( u , & t ); // u の値と変数 t のアドレス (の値) が呼び出しに渡されますC言語とPascal言語の最も重要な違いの一つは、サブルーチン呼び出し時にスタック上のパラメータをどのように扱うかという点です 。これは呼び出し規約 と呼ばれ、Pascalスタイルのパラメータは左から右の順にスタックにプッシュされます。一方、C言語のSTDCALL呼び出し規約では、パラメータは右から左の順にスタックにプッシュされます。
Pascalスタイルのプロシージャ呼び出しは次のように行われます 。
; パスカルスタイルの呼び出しの例。; 注: __stdcall は引数を逆順にプッシュします。push arg1 push arg2 push arg3 call function ; 戻り時にスタックのクリーンアップは行いません: 呼び出し先が行います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
PascalとCにも可変引数I/O関数がいくつかあります。たとえば、WriteLn()およびですprintf()。
Pascalでは、手続きや関数をネストすることができます。これは、手続きのグループ内ではローカルだがグローバルではない変数を扱う場合に便利です。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 );Pascalでは、関数やプロシージャをパラメータとして関数やプロシージャに渡すことができます。
procedure ShowHex ( i : integer ) ; ... end ;procedure ShowInt ( i : integer ) ; ... end ;プロシージャデモ(プロシージャShow ( i : integer )) ; var j :整数; begin Show ( j ) end ;...デモ( ShowHex ) ;デモ( ShowInt ) ; ...初期のC言語には定数宣言も型宣言もなく、メモリ使用量を抑えるために定数、インクルード、マクロ定義を処理する「プリプロセッサ」という別個のプログラムと処理パスを必要とする言語として定義されていました。その後、ANSI Cの導入により定数と型定義の機能が加わり、プリプロセッサも言語の一部となり、今日見られる構文へと発展しました。
Pascal の定数と型定義は組み込みであり、プリプロセッサは必要ありません。Pascal でもプリプロセッサを使用するプログラマもいましたが (C で使用されていたものと同じものもありました)、C ほど一般的ではありませんでした。Pascal の「欠点」としてよく指摘されますが、技術的には C にもプログラムのモジュール性やマクロは組み込まれていません。ただし、C には (従来アセンブリ言語で使用されていたのと同じ汎用リンカを使用して) 単純な低レベルの分離コンパイル機能がありますが、Pascal にはありません。
C言語では、プログラマはポインターを使って任意のオブジェクトのバイトレベルの表現を調べることができますchar。
int a ; char * p = ( char * )( &a a ); char c = * p ; // a の最初のバイトPascalでは、無差別変異体レコードを使用することで同様のことが可能かもしれない。
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の標準化によって義務付けられていません。Pascalの設計者であるニクラウス・ヴィルトは、この方法で型エスケープを試みることの問題点について次のように述べています。
「Pascalの実装者のほとんどは、このチェックはコストがかかりすぎ、コードが肥大化し、プログラムの効率が低下すると判断した。その結果、バリアントレコードは、トリックを好むプログラマーにとって型システムを突破するための好都合な機能となったが、それはたいてい落とし穴や災難につながる。」
現在では、Java、C#、そしてWirth自身が開発したOberonなど、いくつかの言語がそのような型エスケープを明示的に除外しています。
C言語では、ファイルは組み込み型として存在せず(システムヘッダーで定義される)、すべての入出力はライブラリ呼び出しを介して行われる。一方、Pascal言語にはファイル処理機能が組み込まれている。
各言語で入出力を行う際に一般的に使用される文は以下のとおりです。
C言語では:
printf ( "合計は: %d \n " , x );Pascalでは:
writeln ( '合計は:' , x ) ;主な違いは、C言語ではprintf関数の引数を見つけて変換するために「フォーマット文字列」が解釈されるのに対し、Pascalでは言語プロセッサの制御下でそれが行われる点です。Pascal方式は解釈が行われないため、おそらく高速ですが、C言語方式は拡張性に優れています。
人気のある Pascal 実装の中には、ほぼすべての C 構文を Pascal に組み込んでいるものがあります。例としては、型キャスト[ 2 ]、ローカルまたはグローバルの任意の変数のアドレスを取得できること、特別な昇格特性を持つさまざまな型の整数などがあります。
しかし、C言語の型と型変換に対する寛容な姿勢を取り入れると、Pascalは型セキュリティの一部または全部を失う可能性があります。例えば、JavaとC#は、C言語の型セキュリティ上の問題点に対処するために開発されたものであり、無効な参照を作成するために使用できない「管理ポインタ」を備えています。Niklaus Wirthが説明したように、Pascalは本来、JavaやC#よりも約30年も前に管理ポインタ言語として分類されていました。しかし、C言語と統合されたPascalは、定義上、その保護を失います。一般的に、基本的なタスクにおけるポインタへの依存度が低いため、実際にはC言語よりも安全です。
拡張Pascal規格は、Pascalを拡張し、元のPascal規格ではサポートされていなかったC言語の多くの機能を、より安全な型でサポートします。例えば、スキーマ型は(他の用途に加えて)可変長配列をサポートしつつ、配列の次元を配列と共に必ず保持するという型安全性を維持することで、動的にサイズ変更される配列に対しても、範囲外インデックスの自動実行時チェックを可能にします。