Visual C++.NETでデータを処理して図にする、というプログラムを作っています。
int data[N][N];
でNが1010程度を超えると、ハンドルされていない例外 が発生し、stack overflow と表示され、全くプログラムが走りません。
#define N 1000; としていて、他にもオーダーNの配列がいくつかあるので単純にスタックを2倍にすればよいというものでもないかもしれませんが、コンパイルオプションで /F 3 としても全く変化無しです…。
どう対処していいのか分かりません。(new deleteを使うことも考えたのですが、プログラムの流れ上、deleteの有効なタイミングがありません)。
スタックサイズ変更に限らず、stack overflowになったときのよい対処法をご存知の方がいたら教えて戴きたく思います、よろしくお願いします。
スタックサイズ変えるよりも,構造の見直し(Stack→Heap/Static)が先ですね。
> (new deleteを使うことも考えたのですが、プログラムの流れ上、deleteの有効なタイミングがありません)
?
普通newしたものってスマートポインタに突っ込むからdeleteってデストラクタに任すものでは?
まぁ,今回の場合はstd::vector<std::vector<int> >あたりが手っ取り早いですが。
> #define N 1000; としていて、他にもオーダーNの配列がいくつかあるので単純
> にスタックを2倍にすればよいというものでもないかもしれませんが、コンパイル
> オプションで /F 3 としても全く変化無しです…。
/F オプションは、バイト単位で設定すると思います。
その辺を確認してください。
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/vccore/html/_core_.2f.f.asp
ご教授いただき、ありがとうございます。
Eightyさんの教えてくださったのと同じページを見て/F 3としたのですが…。defaultで1MBということなので3MBにしようと思ったのですが…。
/F 3000000000000としても全く変わりません(やはりコンパイルはできて、実行されない)。
YuOさんに教えていただいたものはちょっと知識が追いつかず理解できないのですが(知識レベルが初心者なので)。。。で、検索してみて、
http://www.kab-studio.com/Programing/Dictionary/index.html
を見つけました。ちょっとは分かりそうです。
>普通newしたものってスマートポインタに突っ込むからdeleteってデストラクタに任すものでは?
デストラクタは例外処理の時に有効なのでしょうか?
mallocとfree と同じ働きのものとして newとdeleteを考えていました。
…まずは構造の見直しをしてみることにします。
> Eightyさんの教えてくださったのと同じページを見て/F 3としたのですが…。defaultで1MBということなので3MBにしようと思ったのですが…。
> /F 3000000000000としても全く変わりません(やはりコンパイルはできて、実行されない)。
このバイト数、大き過ぎないですか?
これでは3テラバイトかと。
3Mバイトでためされましたか?
http://www.infonet.co.jp/ueyama/ip/glossary/byte.html
>Eightyさん
/F 3000000でやっても変化なしだったので大きすぎるのでやってみました(前の発言では言葉が足りなかったですね、すみません)。
MSDNなどで調べると、これを変更するのは最終手段のようなので、まずは構造を見直そうと思いました(にしても3TBとしてもcompileは通ってしまうものなのですね・・・もしくは全くStackSizeが変更できてないのか)。
ためしにどんな絵になるか描くプログラムで、何度も走らせるものではないから効率を無視してでっかい配列を扱っていたのですが、関数内でそのようなでかい変数用のメモリを確保するとstack領域に収まらずあのような事態になるようです。なので関数外(外部)で次のようなクラスを定義し、
class Cood{ public:
int dat[N][N];
Cood(){for(int i=0;i<N;i++)for(int j=0;j<N;j++)dat[i][j]=0;}};
関数内で、 Cood *al=new Cood; と動的?に確保?して
値が必要なときは (*al).dat[i][j]とし、
もう必要なくなったときに delete al;としたらうまく行きました。
(N=2300としても上手く行きました!)
とりあえずは(私のしたかったことは)解決しました(どうも有難う御座いました)。がスタックサイズの変更は、、、できてないのでしょうか??
(なので"解決"としていいものやら…)。
> デストラクタは例外処理の時に有効なのでしょうか?
有効です。
例外に伴う巻き戻しでは,巻き戻る途中に生成されたauto変数は全て破棄されます。
当然,その変数にデストラクタがあれば,デストラクタが呼び出されます。
でもって,リソース(memory, file pointer, Windows handle, etc.)を取得した場合,
それを何らかのクラスのオブジェクトに代入して,
そのクラスのデストラクタでリソースの解放を行う,というのは一般的な手段です。
やっていることを把握するために非常に簡単なスマートポインタを作ると,
template <typename T> class SmartPointer {
// 複製は禁止
SmartPointer (const SmartPointer &);
SmartPointer & operator= (const SmartPointer &);
T * pointer_;
public:
SmartPointer (T * pointer = 0) : pointer_(pointer) {}
~SmartPoitner (void) { delete pointer_; }
SmartPointer & operator= (T * pointer) {
if (pointer_ != pointer) {
delete pointer_;
pointer_ = pointer;
}
return *this;
}
T * operator-> (void) const { return pointer_; }
T & operator* (void) const { return *pointer_; }
T * get (void) const { return pointer_; }
bool operator! (void) const { return pointer_ == 0; }
};
なんてところでしょうか。
この延長で,Windows NT Kernel Handleを扱うためのクラスを用意すると,
class SmartHandle {
SmartHandle (const SmartHandle &);
SmartHandle & operator= (const SmartHandle &);
HANDLE handle_;
public:
SmartHandle (HANDLE handle = 0) : handle_(handle) {}
~SmartHandle (void) { if (isValid()) CloseHandle(handle_); }
SmartHandle & operator= (HANDLE handle) {
if (handle_ != handle) {
if (isValid()) CloseHandle(handle_);
handle_ = handle;
}
return *this;
}
// 若干isValidが違うかも……。
bool isValid (void) const { return handle_ != 0 && handle_ != INVALID_HANDLE_VALUE; }
bool operator! (void) const { return !isValid(); }
HANDLE get (void) const {return handle_; }
};
なんてことになります。
こういうクラスを利用することには,
+後処理のためのコードが分散したり,後処理のためにgoto使ったり,が不要になる
+例外が発生した場合に,わざわざcatch (...)を使って後処理をする必要が不要になる
−クラス自体にバグがあるとデバッグが困難
−大域変数が変化する可能性がある(ex:GetLastErrorの戻り値等)
といった利点/欠点があります。
> ためしにどんな絵になるか描くプログラムで、何度も走らせるものではないから効率を無視してでっかい配列を扱っていたのですが、関数内でそのようなでかい変数用のメモリを確保するとstack領域に収まらずあのような事態になるようです。
> なので関数外(外部)で次のようなクラスを定義し、
> class Cood{ public:
> int dat[N][N];
> Cood(){for(int i=0;i<N;i++)for(int j=0;j<N;j++)dat[i][j]=0;}};
汎用的には,boost::arrayになるんでしょうね。
私のようにboostの使えない環境(VC++ 5.0……(泣))では,
template <typename T, unsigned N> struct array {
T data[N];
operator T * (void) { return data; };
operator const T * (void) const { return data; }
T & operator[] (int index) { return data[index]; }
const T & operator[] (int index) const { return data[index]; }
];
あたりを用意することになります。Coodは,
array<array<int, N>, N>
を利用して処理することになるかと。
> 関数内で、 Cood *al=new Cood; と動的?に確保?して
> 値が必要なときは (*al).dat[i][j]とし、
> もう必要なくなったときに delete al;としたらうまく行きました。
> (N=2300としても上手く行きました!)
スマートポインタを使って
typedef array<array<int, N>, N> ArrayType; // あまりにわかりにくい型なのでtypedefした
std::auto_ptr<ArrayType> al(new ArrayType); // 標準のスマートポインタstd::auto_ptr
memset(&(*al)[0][0], 0, sizeof(ArrayType)); // 0で埋める
とすると,delete al;は書く必要が無くなります。
operator[]を用意してやることで,(*al)[0][0]が許されます。
YuOさん、ご親切に+αのことまで教えて戴き、有難う御座いました。
最近C++を勉強し始めたばかりで、operator関数や、スマートポインタ等、
全くの初耳でした(お恥ずかしながら…)。とても勉強になりました。
今後も何かありましたら是非ご教授願います。
一応 解決…ということにしておきます。
(解決してないけど 未解決にしておくのも気持ち悪いので…)
Microsoft Visual Stdio.Net/Vc7/bin
にあるeditbin.exeという実行ファイルを使えば、任意の実行ファイルのスタック領域を変更できるようです。具体的にはコマンドラインから、
>editbin/STACK:3000000 program.exe
とすればよいようです(人に教えてもらいました。その人も/F 30000000では
変更できなかったみたいです)。
これで本当に解決 ですね。