みなさんはじめましてよろしくお願いします。asapといいます。
さて、早速質問をお願いします。
今、csvファイルからグラフを書くプログラムを作成中です。
目標は複数のグラフに対して別のウィンドウ内のパラメーターを
変更すると各グラフが再描画してくれるというものです。
とりあえずひとつのパラメーターウインドウ(CRestSimulationRom)と
グラフウィンドウ(CRestSimulationView)で作成しました。
CRestSimulationRomからCRestSimulationViewの関数、変数にアクセス
またはその逆をしたいのですがうまくいきません。
環境 VC++ 6.0
MFCで開発
構成 MDI、CFormView。
CRestSimulationDoc //ウイザードで出来たクラス
CRestSimulationView //ウイザードで出来たクラス ダイアログがある
CRestSimulationRom //自分で作成したクラス ダイアログがある
いろいろ調べてとりあえず2つの方法を試してみましたがコンパイルは
できるのですがデバック時にエラーが出てしまいます。
以下 CRestSimulationRomクラス内での呼び出し
方法1
CRestSimulationView *MyView = (CRestSimulationView*)((CMDIFrameWnd*)
AfxGetMainWnd())->GetActiveFrame()->GetActiveView();
MyView->GraphON = 1 ; // CRestSimulationView のint型 グローバル変数
グラフ描画確認用変数
MyView->Calc_Call(); // CRestSimulationView の自作関数
計算
MyView->GraphWrite(); // CRestSimulationView の自作関数
実際にグラフを書く
MyView->UpdateData(FALSE);
デバックを行うと下記のアラームが出てしまいます。
例外処理 (初回) は RestSimulation.exe にあります: 0xC0000005: Access
Violation。
方法2
CRestSimulationView RestSimulationView;
RestSimulationView.GraphON = 1 ;
RestSimulationView.Calc_Call();
RestSimulationView.GraphWrite();
RestSimulationView.UpdateData(FALSE);
デバックを行うと下記のアラームが出てしまいます。
例外処理 (初回) は RestSimulation.exe にあります: 0xC00000FD: Stack Overflow。
説明が不足しているかも知れませんがなにとぞよろしくお願いします。
0xC00000FD: Stack Overflow
とりあえず、スタックオーバーフローです。
これがどんなモノかは調べて貰うとして、原因は次の通り。
① 関数内の変数(auto変数)のバイト数が多すぎる。
② 関数コールのネストが深すぎる。(再帰関数で良く起きる)
③ スタック破壊をして、処理自体が何処かに飛んで行った。(たまたまその場所でループした)
普通有力なのは①②です。
③は特殊ケースですが、、、
そもそもバックトレースはどうなってますか?
それじゃ、私は方法1について・・・
おそらく、例外の内容から察するに・・・
CRestSimulationView *MyView = (CRestSimulationView*)((CMDIFrameWnd*)
AfxGetMainWnd())->GetActiveFrame()->GetActiveView();
が悪さしてると思います。
AfxGetMainWnd
GetActiveFrame
GetActiveView
のどれかがNULLを返してるんじゃないでしょうか?
個人的には、確実にウィンドウが生成されていない可能性のある場所で、
上記のような記述は危険だと感じます。
デバッグが容易になるよう、それぞれを分割して処理してみてはいかがでしょう?
たとえば以下のような感じに・・・
-----
CWnd* pMainFrame = AfxGetMainWnd();
ASSERT(pMainFrame);
CFrameWnd* pActiveFrame = STATIC_DOWNCAST(CFrameWnd,pMainFrame)-
>GetActiveFrame();
ASSERT(pActiveFrame);
CRestSimulationView * pView = STATIC_DOWNCAST
(CRestSimulationView,pActiveFrame->GetActiveView());
ASSERT(pView);
----
CRestSimulationRomの基本クラスはCFormViewでしょうか?それともCDialog?
もし、このクラスがCFormView(又はCView)クラスから派生されたものであれば、
CDocument::UpdateAllViews, CView::OnUpdate を使用するのが、
一番目的にあっているかもそれません。
> CRestSimulationView *MyView = (CRestSimulationView*)((CMDIFrameWnd*)
> AfxGetMainWnd())->GetActiveFrame()->GetActiveView();
もしCRestSimulationRomがCFormViewなら、自分自身を取得していたりして
みなさんいろいろとアドバイスありがとうございます。
Bosscat さんレスありがとうございます。
>とりあえず、スタックオーバーフローです。
>これがどんなモノかは調べて貰うとして、原因は次の通り。
>① 関数内の変数(auto変数)のバイト数が多すぎる。
>② 関数コールのネストが深すぎる。(再帰関数で良く起きる)
>③ スタック破壊をして、処理自体が何処かに飛んで行った。(たまたまその場所でルー
>プした)
いちおう調べてみたのですがいまいち理解できなかったです。
1は確保している変数名が多い? それとも宣言した変数の数が多いのでしょうか?
2は宣言の部分が1行で長すぎたからでしょうか?
変数のオーバーフローとかという意味では
csvファイルを取り込むのに下記のような大きな変数をviewで確保しているのは
確かなんですが、直接は関係してないので大丈夫かと思ってます。
int line_0[20000][10];
int line_1[20000][10];
int line_2[20000][10];
int line_3[20000][10];
あいるさん レスありがとうございます。
> CWnd* pMainFrame = AfxGetMainWnd();
> ASSERT(pMainFrame);
> CFrameWnd* pActiveFrame = STATIC_DOWNCAST(CFrameWnd,pMainFrame)-
>GetActiveFrame();
> ASSERT(pActiveFrame);
ブレークで区切って確認をすると
ここでアラームがおきてるようです。
Debug Libraryのダイアログが現れて
Program:・・・・・
File:objcore.cpp
Line:63
というメッセージがでます。
あとVC6の下の方の窓には下記のようなメッセージが延々と流れてしまってます。
Detected memory leaks!
Dumping objects ->
strcore.cpp(118) : {329786} normal block at 0x0037BEC0, 45 bytes long.
Data: < 1230> 01 00 00 00 05 00 00 00 20 00 00 00 31 32 33 30
REEさんレスありがとうございます。
>CRestSimulationRomの基本クラスはCFormViewでしょうか?それともCDialog?
>もし、このクラスがCFormView(又はCView)クラスから派生されたものであれば、
>CDocument::UpdateAllViews, CView::OnUpdate を使用するのが、
>一番目的にあっているかもそれません。
すみません説明不足でした。
CRestSimulationRomの基本クラスはCFormViewです。
>もしCRestSimulationRomがCFormViewなら、自分自身を取得していたりして
そうなんですか?
自分自身を呼び出してしまってるのですか?
CRestSimulationRomと指定しているのうまく取得してるいるのかと
思ってました。
その関係でもし、関係するかわかりませんがウインドウを作成している
ところを追記します。
AppクラスのInitInstance()で下記のような方法で
起動時にウインドウを2つ作ってます。
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_RESTSITYPE,
RUNTIME_CLASS(CRestSimulationDoc),
RUNTIME_CLASS(CChildFrame), // カスタム MDI 子フレーム
RUNTIME_CLASS(CRestSimulationView));
AddDocTemplate(pDocTemplate);
//ROMウインドウ追加
pDocTemplate = new CMultiDocTemplate(
IDR_RESTSITYPE,
RUNTIME_CLASS(CRestSimulationDoc),
RUNTIME_CLASS(CChildFrame), // カスタム MDI 子フレーム
RUNTIME_CLASS(CRestSimulationRom));
AddDocTemplate(pDocTemplate);
// メイン MDI フレーム ウィンドウを作成
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
// DDE、file open など標準のシェル コマンドのコマンドラインを解析します。
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// コマンドラインでディスパッチ コマンドを指定します。
//起動時にウインドウを選ぶダイアログが出てしまうため無効。
//if (!ProcessShellCommand(cmdInfo))
//return FALSE;
// メイン ウィンドウが初期化されたので、表示と更新を行います。
pMainFrame->ShowWindow(SW_SHOWMAXIMIZED);
pMainFrame->UpdateWindow();
POSITION pp = GetFirstDocTemplatePosition();//起動時に一番目のウインドウを表示
GetNextDocTemplate(pp)->OpenDocumentFile(NULL);
POSITION p = GetFirstDocTemplatePosition();//起動時に二番目のウインドウを表示
GetNextDocTemplate(p);
GetNextDocTemplate(p)->OpenDocumentFile(NULL);
//横に並べて表示
CMDIFrameWnd* p3 =(CMDIFrameWnd*)AfxGetMainWnd();
p3->MDINext();
p3->MDITile(MDITILE_VERTICAL);
すみませんがよろしくお願いします。
VC のデフォルトでは、スレッド毎のスタックサイズは 1MB です。
自動変数(普通に関数内などに定義した変数)のサイズや、
関数呼び出しなどに使うサイズの合計が上記のサイズを超えると、
スタックはオーバフローします。
> int line_0[20000][10];
> int line_1[20000][10];
> int line_2[20000][10];
> int line_3[20000][10];
20000*10*sizeof(int)*4...3MBくらいありませんか?
本当に無関係なのでしょうか。
再帰呼び出しの場合には、スタックサイズが 4MB でも 5MB でも確実に
オーバフローしますので、これが原因かどうかは VC のオプション ( /F )で
スタックサイズを変更してもっと増やしてみれば切り分け出来ると思います。
# 尚、この場合の修正方法としては、処理を見直すか、スタックサイズを増やすか、
# 動的に確保したサイズはスタックとは別計算なので new などで確保するか、です。
Ban さんこんばんはレスありがとうございます。
>VC のデフォルトでは、スレッド毎のスタックサイズは 1MB です。
>自動変数(普通に関数内などに定義した変数)のサイズや、
>関数呼び出しなどに使うサイズの合計が上記のサイズを超えると、
>スタックはオーバフローします。
int line_0[20000][10];
行 1から10列
int line_1[20000][10];
11から20列
int line_2[20000][10];
21から30列
int line_3[20000][10];未使用
これに行き着いた経緯なんですが、はじめは
int line[10000][30];
みたいなことを行ってました。
これだとcsvファイルが5000行位のものを読み込むとスタックオーバーフロー
してしまって困った挙句上記のように変更しました。
10列ごとに変数を切り替えるように変更したところ10000万行以上
読み込んでもエラーが出なくなったのでこれで解決できたかと思ってましたが
やはり強引でしたでしょうか?
すみません、また説明不足でしたが、ひとつのviewクラスとdocクラスの
構成ではグラフ描画はできてました。
今回、そのうちパラメーターの変更部分のみ独立したウインドウに
しようとして失敗してるところです。
あと、他にtestプログラムで確認しても同じようにうまくできなかったので
int line_0[20000][10];とかは関係ないかと判断してました。
testview
testdoc
test1
再帰呼び出しの場合には、スタックサイズが 4MB でも 5MB でも確実に
オーバフローしますので、これが原因かどうかは VC のオプション ( /F )で
スタックサイズを変更してもっと増やしてみれば切り分け出来ると思います。
# 尚、この場合の修正方法としては、処理を見直すか、スタックサイズを増やすか、
# 動的に確保したサイズはスタックとは別計算なので new などで確保するか、です。
すません。
途中で誤送信してしまいました。
以下途中からです。
あと、他にtestプログラムで確認しても同じようにうまくできなかったので
int line_0[20000][10];とかは関係ないかと判断してました。
testview ボタンをおすとeditにtestと表示される
testdoc
test1 ボタンをおすとtestviewのボタンを呼び出す
のような構成にして試したところ同じようにエラーが出てしまったので
viewの呼び出し方が駄目なのかと思ってました。
>オーバフローしますので、これが原因かどうかは VC のオプション ( /F )で
># 尚、この場合の修正方法としては、処理を見直すか、スタックサイズを増やすか、
># 動的に確保したサイズはスタックとは別計算なので new などで確保するか、です。
すみませんこの辺は勉強不足で色々と調べてもわからなかったの上記のように
したらうまくいったのでそれですませてました。
動的に確保するならvectorあたりを使って
std::vector<std::vector<int> > line;
と宣言する.
数千行単位のデータを扱うのが確実ならコンストラクタあたりで
line.reserve(5000);
でもしておけばよさそう
Kazuki さんレスありがとうございます。
>std::vector<std::vector<int> > line;
>と宣言する.
>line.reserve(5000);
すみませんおそらく初めてみる言葉です。
std::vector<std::vector<int> > line;
はviewのpublickあたりでせんげんして。
line.reserve(5000);はどこかの初期化の
関数で呼べばよいのでしょうか?
あと、プログラムでは4列目と5列目のデータを呼んで計算して
7列目に書くとかを行ってるのですがどうやって個別の列を
読み書きするのかわからないです。
勉強不足ですみません。
明日調べて試してみます。
> 再帰呼び出しの場合には、スタックサイズが 4MB でも 5MB でも確実に...
念のため補足。意図的に再帰させている(抜け条件がある)ような場合はそうとは限りません。
> 10列ごとに変数を切り替えるように変更したところ10000万行以上
> 読み込んでもエラーが出なくなったのでこれで解決できたかと思ってましたが
> やはり強引でしたでしょうか?
スタックサイズは、同時に使える作業用メモリのサイズとでも思ってください。
関数が分かれていても、内部で別関数を呼び出せばそのサイズは合算して
考えますし(最初の関数の分も全部、戻った後のために保存しておくため)、
データ全体のサイズがどれだけあろうと、同時にメモリ上に確保するサイズが
制限以下ならスタックは溢れません。
ですから、最初に書かれていたように並べて宣言されているなら合算しますが、
実は別々のタイミングで(10行分づつ)処理しているなら多分大丈夫です。
# ちょっと大きなデータを追加すると溢れそうなサイズなので、
# 後々ハマらないためにも留意はしておいた方がいいと思いますけど。
> CDocument::UpdateAllViews, CView::OnUpdate を使用するのが、
> 一番目的にあっているかもそれません。
構成を聞く限り、MFC が提供する仕組みをそのまま使うのが一番
綺麗だと思います。個人的には私もREEさんの意見に賛成です。
>>もしCRestSimulationRomがCFormViewなら、自分自身を取得していたりして
>そうなんですか?
>自分自身を呼び出してしまってるのですか?
>CRestSimulationRomと指定しているのうまく取得してるいるのかと
>思ってました。
「アクティブなView 」が取れるだけですからその可能性は高そうです。
ちなみに、Cスタイルでポインタをキャストすると、
本当にそのクラスであるかにかかわらずキャストは成功します。
その結果、実は Rom なのに View のメンバ関数を呼び出せば、
.....大抵不正アクセスで落ちますね。Access Violation はこれでしょうか。
RUNTIME_CLASS や dynamic_cast 等を使用を検討してみてください。
あと、方法2の方ですが、
> 以下 CRestSimulationRomクラス内での呼び出し
> CRestSimulationView RestSimulationView;
>
> RestSimulationView.GraphON = 1 ;
> RestSimulationView.Calc_Call();
> RestSimulationView.GraphWrite();
> RestSimulationView.UpdateData(FALSE);
と書いてありますが、もしかして RestSimulationView という
View のインスタンスをここに作ったということでしょうか。
とすると、例の配列一つでも800KB くらいはありますから、
呼出全体で 1MB を超えたりしてませんか。
例の配列が2つ存在すれば確実にアウトですし、そうでなくとも
他のデータもそれなりにサイズがあると思います。
Stack overflow はこれじゃないでしょうか。
> すみませんおそらく初めてみる言葉です。
C++言語標準のSTL コンテナの一つです。
あまりメモリを意識しなくてすむため確かにこちらの方が安全かもしれません。
使い方の方は配列とほぼ互換なので、初期化だけちゃんとやれば
foo[7] = foo[5] + foo[4]; みたいに書けます。詳細は調べてみてください。
まあ、vectorやmapのようなSTLに頼るのが1番スッキリしますね。
ただ、現状のソースから、データ用の領域をスタックからヒープに
移してやれば取り敢えず最短で解決します。
つまり、new や mallocで領域を取れば終わりです。
思うにC言語とかC++言語の変数のスコープと確保される場所に関する記述を
読み直されたほうがよいと思います。
もし入門書の類を通読した事がないということであれば、
言語の入門書を購入して演習問題も含めて通読される事をお勧めします。
こういった基礎知識はとても大事なのにどうしても軽視されがちです。
プログラミングは、OSの知識、言語の知識、開発環境の知識が必要です。
全てそろってないと自分では理解できない不具合に悩まされることが多いと思います。
とにかく、不具合がおきた時は原因を深く掘り下げる癖をつけましょう。
対処療法だけで直した不具合ほど怖い物はありません。
原因究明できていない不具合は解決したとは言えません。
みなさん、丁寧な解説ありがとうございます。
今日は他の仕事が忙しかったもので色々と試すことができませんでした。
もうしわけありません。
Ban さん
>スタックサイズは、同時に使える作業用メモリのサイズとでも思ってください。
このへんの認識が微妙でした。
サイズ大きい変数があると駄目なものとおもってました。
だから、変数を分ければよいものと思ってました。
実際の処理は
line_0などの変数をviewのグローバルで確保して
csvread という関数でファイルから全行のデータを読み込み line_0などの変数にと
りこんで
calc という関数でline_0 の内容を1行ずつ計算して書き換えし
graphwrite という関数でグラフを描画してました。
スタックというのは簡単に言えばひとつの関数単位で扱えるメモリサイズと考えてよい
のでしょうか?
ただ、その関数が他の関数を呼び出していればその分も合算して考える。
> CDocument::UpdateAllViews, CView::OnUpdate を使用するのが、
> 一番目的にあっているかもそれません。
OnUpdate内に希望する処理を書き込んだほうが解決の近道なんでしょうか?
この方法も試してみます。
>その結果、実は Rom なのに View のメンバ関数を呼び出せば、
>.....大抵不正アクセスで落ちますね。Access Violation はこれでしょうか。
そうですかうまくviewの関数を呼び出せなかったのですか?
>RUNTIME_CLASS や dynamic_cast 等を使用を検討してみてください。
ありがとうございます。また難しい言葉が出てきてしまった。
>あと、方法2の方ですが、
> 以下 CRestSimulationRomクラス内での呼び出し
> CRestSimulationView RestSimulationView;
>
> RestSimulationView.GraphON = 1 ;
> RestSimulationView.Calc_Call();
> RestSimulationView.GraphWrite();
> RestSimulationView.UpdateData(FALSE);
>例の配列が2つ存在すれば確実にアウトですし、そうでなくとも
>他のデータもそれなりにサイズがあると思います。
>Stack overflow はこれじゃないでしょうか。
元々は上記の処理はview内の関数で読み出してました。
csvを読み込むと例の配列は3つ程存在してるはずです。
確か24列 1万2千行くらい読み込んで処理できてましたので
romクラスから呼んでも問題ないと思っていました。
>使い方の方は配列とほぼ互換なので、初期化だけちゃんとやれば
>foo[7] = foo[5] + foo[4]; みたいに書けます。詳細は調べてみてください。
すみません2次配列で利用したいのでfoo[3][7] = foo[3][4] + foo[3][4] ;
なんて感じで使えばよいでしょうか_
Bosscat さん
>つまり、new や mallocで領域を取れば終わりです。
このへんは最初に調べて出てきたのですが、難しそうなので敬遠してました。
それで例の対象方法でうまくいったのでそれでよしとしてました。
もう一回利用できるように調べてみます。
あと、本で調べるとかならず開放もしないといけないんですよね?