32

Cプログラミング言語では、非宣言型/割り当て型の式が評価される前に、私が使用したすべての言語リビジョンで、事前の変数宣言が強制されました。C ++は、すべてのバージョンからこの要件を免除したようです。また、Cの最新バージョンでもこの要件が免除されていることを認識していますが、これらの標準はまだ使用していません。

私が持っている質問はこれです:C言語が前もってではなくオンデマンドで自由に宣言することを妨げた歴史的な理由は何でしたか?

明らかに、エンジニアリングの観点から頭に浮かぶ理由はいくつかありますが、どれも私には特にもっともらしいとは思えません。

  1. あいまいなコンパイラの動作エラーの発生を防止します(無限の解析ループ、評価のための大規模なメモリの肥大化、またはマクロの奇妙なコーナーケースなど)。
  2. 望ましくないコンパイラ出力の防止。これは、デバッグプロセスを混乱させるシンボル出力やデバッグツールの開発の容易さから、予期しないスタックストレージの順序まで何でもかまいません。
  3. 読みやすさ。Cは、当時の他の言語と比較して読みやすいように設計されていますが、他のほとんどの場所ではこのタイプの構造を強制していなかったため、これも飲み込むのが難しいと思います。(プロトタイピングが同様の強制であると見なさない限り、ただし、プロトタイプが'89仕様に追加されたことを思い出してください。)
  4. 実装の複雑さと実際的な理由。これは私が最も信じたいと思うものです。エンジニアとして、割り当てられた時間枠で実行可能な製品を出荷するために、特定の考慮事項を考慮する必要があります。コンピュータサイエンスとソフトウェアエンジニアリングの両方の専門的な状況が劇的に変化したことを認めますが、ビジネスは依然としてビジネスです。結局のところ、Bellは、Unixプログラミング環境で使用できる完成品を望んでいたと確信しています。

上記のいずれかを裏付ける良い情報源はありますか?私は何かを完全に見逃しましたか?夜明けから夕暮れまで推測できますが、良い参考資料を探しています。

4

3 に答える 3

19

初期の (第 6 版 Unix、1975 年) Dennis Ritchie のホームページのC マニュアルを見ると、そのバージョンでは、関数ローカル変数は関数の先頭でしか宣言できませんでした。

function-statement は、最初に宣言がある場合がある単なる複合ステートメントです。

function-statement : {宣言リストopt ステートメントリスト}

宣言リストは定義されていませんが (省略)、文法があると容易に想定できます。

宣言リスト:宣言 宣言リストopt

他の複合ステートメントには、変数 (または実際には任意の) 宣言を含めることはできません。

これは明らかに実装を簡素化します。初期のコンパイラ ソース コードc02.cでは、関数ヘッダー関数は、変数宣言blkhed()によって使用されるスタック スペースを合計するだけでよくauto、同時にそれらのスタック オフセットを記録し、適切な量だけスタック ポインターをバンプするコードを発行します。関数returnの終了時 (終了時または終了時) に、実装は保存されたスタック ポインターを復元するだけで済みます。

K&R が「変数の宣言 (初期化を含む) は、関数を開始するものだけでなく、複合ステートメントを導入する左中括弧の後に続く場合がある」と述べる必要があると感じているという事実は、その時点でそれが比較的最近の機能であったことを示唆しています。 . また、宣言と初期化を組み合わせた構文も最近の機能であり、実際に 1975 年の手動宣言子では初期化子を使用できないことも示しています。

1975 年のマニュアルのセクション 11.1 には、具体的に次のように記載されています。

C はブロック構造言語ではありません。これはかなり欠陥と考えられます。

ブロックステートメントと初期化された宣言 (K&R) は欠陥に対処し、混合宣言とコード (C99) は論理的な継続です。

于 2013-01-14T20:11:47.820 に答える
10

C89 では、変数定義はブロックの先頭にある必要があります。(ブロックの定義については、C 標準を参照してください) これは、私が知る限り、変数がアセンブラーで処理される方法を単純化するために行われました。たとえば、単純な関数を見てみましょう。

void foo()
{
    int i = 5;
    printf("%i\n", i);
}

gcc がこの関数をアセンブラー コードに変換すると、foo() の呼び出しは、関数スコープのスタック フレームの設定を含む一連の命令に要約されます。このスタックフレームには、関数のスコープで定義された変数用のスペースが含まれており、高水準言語 C で同じスコープに一致させるには、ブロックの先頭で定義する必要がありました。

最後に、実装の容易さと効率性についてでした。一度に多数の変数を宣言すると、ブロックの先頭でコンパイラがそれらをスタックに一括プッシュできるようになり、約 89 倍になります。これはパフォーマンスの考慮事項でもありました。

もちろん、この答えは恐ろしく単純化されており、なぜこれが行われたのかについて簡単な考えを与えることのみを目的としています. 詳細については、おそらく初期の C89 標準のいくつかのドラフトを読む必要があります。

于 2013-01-14T19:43:06.857 に答える
10

実際にはあまり答えられない短い答え: C 言語は、最初にこの宣言順序の制限をその前身である B 言語から継承しました。残念ながら、なぜB言語でそのようにしたのかはわかりません。

また、初期の C ( 「C リファレンス マニュアル」で説明) では、定数以外の式で変数 (ローカル変数であっても) を初期化することは違法であったことにも注意してください。

int a 5;
int b a; /* ERROR in nascent versions of C */

(補足: CRM の初期化構文には=文字が含まれていませんでした)。一般に、これはコード内変数宣言の主な利点である意味のあるランタイム値を初期化子として指定する機能を効果的に無効にしました。より近代的な C89/90 でも、この制限は集約初期化子に正式に適用されていました (ただし、ほとんどのコンパイラはこれを無視しました)。

int a = 5, b = a;
struct { int x, y; } s = { a, b }; /* ERRROR even in C89/90 */ 

C99 でのみ、あらゆる種類のローカル初期化にランタイム値を使用できるようになりました。これにより、コード内変数宣言の全機能がついに解放されたので、C99 がそれらを導入したのは完全に論理的です。

于 2013-01-14T19:50:50.753 に答える