十分なメモリがないときに、可変長の配列がクラッシュするのを防ぐにはどうすればよいですか?
-
08-10-2019 - |
質問
可変長配列がサポートされる前に、次のように動的に割り当てます。
int foo(size_t n)
{
int *arr = malloc(n * sizeof int);
if (!arr) return ENOMEM; /* not enough memory */
.
. else do stuff with arr[]
.
free(arr);
return 0;
}
可変レングスアレイを使用すると、これでクリーナーに見えるようになりました。
int bar(size_t n)
{
int arr[n];
.
. do stuff with arr[]
.
return 0;
}
だが 今、私は「メモリのない」チェックを持っていません。実際、nが大きすぎるとプログラムはクラッシュします。
nが大きすぎる場合、どうすればバー(n)から優雅に保釈できますか?
解決
状況は、他のローカル変数からまったく変更されていません - このような宣言:
int baz(void)
{
int arr[100000];
.
. do stuff with arr[]
.
return 0;
}
まったく同じ問題があります。 「ソリューション」はいつもと同じです - 深く反発しないでください。また、非常に大きなデータ構造を自動ストレージ期間で割り当てないでください(引き続き使用し続けます malloc()
これらの場合)。 「非常に大きい」という価値は、環境に強く依存します。
言い換えれば、宣言しないでください int array[n];
あなたがそれを知らない限り n
合理的な価値に限定されているため、その最大サイズの配列を、通常の非自由に変更されたタイプアレイとして宣言して喜んでいたでしょう。
(はい、これは、最大必要なサイズで配列を宣言するだけではほとんどないため、さまざまな変更型タイプアレイが最初に表示されるほど有用ではないことを意味します)。
他のヒント
使用しないことでクラッシュするのを防ぐことができます。 :)
真剣に、サイズに強い境界線がない限り、可変長の配列を使用して生活を容易にする安全な方法はほとんどありません。一方、このような方法で条件付きで使用できます。
char vla_buf[n < 1000 ? n : 1];
char *buf = sizeof vla_buf < n ? malloc(n) : vla_buf;
if (!buf) goto error;
/* ... Do stuff with buf ... */
if (buf != vla_buf) free(buf);
これは役に立たない痛みのように見えますが、特に多くの呼び出しがあるスレッドアプリケーションでは、大きなパフォーマンスの違いをもたらす可能性があります malloc
と free
ロックの競合につながる可能性があります。 (このトリックの顕著な副次的な利点は、単に交換するだけでVLAなしで古いコンパイラをサポートできることです [n < 1000 ? n : 1]
と 1000
, 、たとえばマクロで。)
VLAが有用である可能性のある別の不明瞭なケースは、再帰アルゴリズムにあります。ここでは、すべてのレベルの再帰において必要な配列エントリの総数が境界を獲得していることがわかります。 n
, 、 どこ n
スタックにオーバーフローしないと確信していますが、 n
再帰のレベルと、最大で使用する個々のレベル n
要素。 C99の前に、服用せずにこのケースを処理する唯一の方法 n^2
スタックスペースが使用されました malloc
. 。 VLASを使用すると、スタックで問題を完全に解決できます。
VLAが本当に有益であるこれらのケースは非常にまれであることに留意してください。通常、VLAは、メモリ管理が簡単であることを欺く方法に過ぎません。 :-)
編集: OPの元の質問にもっと対処するには:
#define MAX_VLA 10000
int bar(size_t n)
{
int arr[n <= MAX_VLA ? n : 1];
if (sizeof arr/sizeof *arr < n) return ENOMEM;
/* ... */
return 0;
}
現実には、どこでも記憶条件から外れていることをチェックするのは非常に高価です。大規模なデータに対処する企業の方法は、シングルの初期チェックポイントでサイズのハードキャップを定義し、キャップがヒットしたときに高速かつ優雅に失敗することにより、データサイズを制限することです。
私が提案したことは、シンプルで愚かです。しかし、それはすべての普通の(科学的または特別ではない)製品が常に行うことです。そして、それは通常顧客に期待されるものです。