4

C 標準では、多くの下限/上限 (変換制限) が定義されており、各変換に対して実装が満たす必要があることが課されています。配列サイズの最小制限が定義されていないのはなぜですか? 次のプログラムは正常にコンパイルされ、実行時エラー/セグメンテーション エラーが発生し、未定義の動作が発生する可能性があります。

int main()
{
   int a[99999999];
   int i;

   for(i=0;i<99999999;i++)
   a[i]=i;

   return 0;
}

考えられる理由は、ローカル配列が自動ストレージに割り当てられ、割り当てられたスタック フレームのサイズに依存している可能性があります。しかし、C で定義された他の制限のような最小制限ではないのはなぜですか?

上記のような未定義のケースは忘れましょう。次の点を考慮してください。

int main()
{
   int a[10];
   int i;

   for(i=0;i<10;i++)
   a[i]=i;

   return 0;
}

上記で、ローカル配列が (非常に小さい配列にもかかわらず) 期待どおりに動作し、割り当ての失敗による未定義の動作を引き起こさないという保証を私に与えるものは何ですか?

このような小さな配列の割り当てが最新のシステムで失敗する可能性はほとんどありません。しかし、C 標準では満たすべき要件が定義されておらず、コンパイラは (少なくとも GCC は) 割り当ての失敗を報告しません。実行時エラー/未定義の動作のみが可能です。難しい部分は、任意のサイズの配列が割り当ての失敗により未定義の動作を引き起こすかどうかを誰も判断できないことです。

この目的のために ( mallocとフレンズを介して) 動的配列を使用でき、割り当ての失敗をより適切に制御できることを認識していることに注意してください。ローカル配列に対してそのような制限が定義されていない理由にもっと興味があります。また、グローバル配列は静的ストレージに格納され、コンパイラが処理できる実行可能サイズが増加します。

4

3 に答える 3

9

あなたはすでに自分の質問に答えています。これはスタック制限によるものです。* これでもうまくいかないかもしれません:

void foo(void) {
    int a;

    ...
}

...が実際に への再帰呼び出しである場合foo

つまり、同じ問題がすべてのローカル変数に影響するため、これは配列とは関係ありません。実際には無限サイズのスタックの要件に変換されるため、標準では要件を強制できませんでした。


* はい、C 標準がスタックについて語っていないことは知っています。しかし、標準が実際には当時存在していた実装の形式化であったという意味で、それは暗黙のモデルです。

于 2013-02-04T20:43:52.367 に答える
9

言語である C は、使用可能なスタック サイズに制限を課すべきではないためです。C は多くの (多くの) 異なる環境で動作します。どうすれば妥当な数を思いつくことができるでしょうか? 地獄、自動保存期間 != スタック、スタックは実装の詳細です。言語であるCは、「スタック」について何も言いません。

環境がこのことを決定しますが、それには正当な理由があります。特定の環境が、そのような制限を課さない代替方法を介して自動保存期間を実装している場合はどうなりますか? ハードウェアのブレークスルーが発生し、突然最新のマシンがすべてそのような制限を必要としなくなったらどうなるでしょうか?

このような場合、基準を改訂する必要がありますか? 言語である C がそのような実装の詳細を指定している場合は、そうする必要があります。

于 2013-02-04T20:46:10.257 に答える
2

MINIMUM制限は、1つの要素の配列です。なぜあなたはそのための「限界」を持っているのでしょうか?もちろん、関数を永久に再帰的に呼び出すと、1の配列がスタックに収まらないか、関数を呼び出す次の呼び出しがスタックに収まらない可能性があります。これを解決する唯一の方法は、コンパイラのスタックのサイズ-しかし、コンパイラはその段階でスタックの大きさを実際には知りません-非常に複雑な呼び出し階層の問題は、複数の異なる関数が同じ関数を呼び出すことであり、おそらく再帰や再帰を伴う可能性があります。スタックのかなり大きな消費者のいくつかの層-そのためにスタックのサイズをどのように設定しますか-他のことがこれが起こらないことを指示するため、最悪のケースに遭遇することはないかもしれません-たとえば、ある関数の最悪のケースは、入力ファイルが空の場合のみですが、別の関数の最悪のケースは、同じファイルに大量のデータが格納されている場合です。このようなバリエーションがたくさんあります。判断するのは信頼性が低すぎるため、遅かれ早かれ、推測作業または多くの誤検知になります。

何千もの関数を備えたプログラムを考えてみましょう。これらの関数はすべて、ログ出力を一時的に保存するためにスタック上に200バイトの配列を必要とする同じロギング関数を呼び出します。これは、メインから上に向かってほぼすべての関数から呼び出されます。

ローカル変数のMAXIMUMはスタックのサイズに依存します。これは、前述のように、コードをコンパイルするときにコンパイラーが認識しているものではありません[リンカーは知っているかもしれませんが、それは後でわかります]。グローバル配列とヒープに割り当てられた配列の場合、制限は「プロセスが取得できるメモリの量」であるため、上限はありません。

これを判断する簡単な方法はありません。また、標準によって提供される制限の多くは、コードがルールに従っている限り、コードを「任意のコンパイラ」でコンパイルできることを保証するためのものです。コンパイルして完了まで実行できるようにすることは、2つの異なることです。

int main(){while(1); }

完全に実行されることはありませんが、私が知っているすべてのコンパイラでコンパイルされ、無限ループがあることについてはほとんど何も言われません。それを行うのはあなたの選択です。

大きなアレイをスタックに配置することも選択できます。そして、リンカに数ギガバイトのスタックが与えられている可能性があります。その場合は問題ありません。または、スタックが200Kであり、50000の整数配列を持つことはできません...

于 2013-02-04T20:54:54.780 に答える