シナリオは次のとおりです。配列を宣言していて、プログラムのどこでも使用していません。長さが100以下であると宣言すると、正常に機能します。長さが長くなると、セグメンテーション違反が発生します。この振る舞いが理解できません。
ATL_RAM_BUFFER_L3 an_unknown_array [128];
ここで、ATL_RAM_BUFFER_L3はほぼ72KBの構造です。
シナリオは次のとおりです。配列を宣言していて、プログラムのどこでも使用していません。長さが100以下であると宣言すると、正常に機能します。長さが長くなると、セグメンテーション違反が発生します。この振る舞いが理解できません。
ATL_RAM_BUFFER_L3 an_unknown_array [128];
ここで、ATL_RAM_BUFFER_L3はほぼ72KBの構造です。
通常は限られたリソースであるスタックを吹き飛ばしています。この場合、アレイが占める9メガバイトほどを保持するには不十分です。
考えられる解決策がいくつかあります。コピーが1つだけ必要な場合は、関数の外部で(または関数内の静的なものとして)宣言できます。これにより、通常はスタックから外され、ストレージが制限されていない領域に配置されます。
または、動的に割り当てて、適切に管理することもできます。
環境によっては、より大きなスタックサイズを構成できる場合もあります。
例として、次のbash
スクリプトは、(デフォルトの)環境でスタックに割り当てることができるデータの量を計算します。
((sz = 0))
((delta = 1000000))
while [[ ${delta} -gt 0 ]]; do
while true; do
sleep 1
((sz += delta))
printf "Trying ${sz} ... "
rm -f qq.c
echo "#include <stdio.h>" >>qq.c
echo "int main (void) {" >>qq.c
echo " char buff[${sz}];" >>qq.c
echo " puts (\"hello\");" >>qq.c
echo " return 0;" >>qq.c
echo "}" >>qq.c
gcc -o qq qq.c
trap 'echo "segfault!"' CHLD
rm -f flagfile
( bash -c './qq >flagfile 2>/dev/null' ) >/dev/null 2>&1
if [[ "$(cat flagfile)" == "hello" ]]; then
echo "okay"
else
echo "crashed"
((sz -= delta))
break
fi
done
((delta = delta / 10))
done
これは、次の形式のCプログラムを作成することによって機能します。
#include <stdio.h>
int main (void) {
char buff[XYZZY];
puts ("hello");
return 0;
}
スタックスペースの不足からプログラムが最初にクラッシュする場所を確認するために、場所XYZZY
が徐々に変更されます。私の環境(CygWin)の出力は次のようになります。
Trying 1000000 ... okay
Trying 2000000 ... okay
Trying 3000000 ... crashed
Trying 2100000 ... crashed
Trying 2010000 ... okay
Trying 2020000 ... okay
Trying 2030000 ... okay
Trying 2040000 ... okay
Trying 2050000 ... okay
Trying 2060000 ... okay
Trying 2070000 ... okay
Trying 2080000 ... crashed
Trying 2071000 ... crashed
Trying 2070100 ... crashed
Trying 2070010 ... okay
Trying 2070020 ... okay
Trying 2070030 ... okay
Trying 2070040 ... okay
Trying 2070050 ... okay
Trying 2070060 ... okay
Trying 2070070 ... crashed
Trying 2070061 ... okay
Trying 2070062 ... okay
Trying 2070063 ... okay
Trying 2070064 ... okay
Trying 2070065 ... crashed
つまり、2メガバイトは最大になるところです。
ローカル変数のメモリは、実行時に割り当て/解放されます。それらはスタック上に作成され、すべてのスレッド/プロセスには制限されたスタックがあります。スタックのサイズは、コンパイラとオペレーティングシステムに大きく依存します。システムによって異なります。Unix / Linuxタイプのシステムでは、スタックオーバーフローが発生すると、プログラムはセグメンテーション違反を起こします。この主題の詳細については、このドキュメントを参照してください。
一方、グローバル変数のメモリは、プログラムがメモリにロードされるときに割り当てられます。初期化されていない場合はBSSセクションで、ある値に初期化されている場合はデータセクションでメモリが割り当てられます。通常、これらのセクションには、プロセスのスタックよりもはるかに多くのスペースがあります。したがって、この問題を回避するために、一般に、自分のような大きな変数/オブジェクトをグローバルに宣言することをお勧めします。