ページ 11

スタックオーバー

Posted: 2009年11月27日(金) 15:27
by 初心者
いつもお世話になります
プログラムを実行していて、途中で止まってしまいます
変な止まりかただったので、タスクマネージャーを起動して調べると、
起動してからのパフォーマンスにおけるメモリ使用量があっという間に 750MBから1.95GBへ(約2分間)

コードは下に書きますが、自作関数、自作マクロがたくさんありまして、
そのまま載せても意味不明ですから、構造のみを書きます
#include "mine.h"
#include <stdio.h>

void funcX( int *A );
void funcY( int *A, int *B );
void funcZ( int *B, int *C, int *D, int *E );

void handle_array( void )
{
	int A [6000];
	int B [6000];
	int C [6000];
	int D [6000];
	int E [6000];
	
	int td, shif;

	for( td = -24; td <= 24; td ++ )
	{
		printf( "td = %d\n", td );
		if( td >= 0 )
		{
			for( shif = 0; shif <= 24 - td; shif ++ )
			{
				funcX( A );
				funcY( A, B );
				funcZ( B, C, D, E );
			}
		}
		else // td < 0.
		{
			for( shif = -td; shif <= 24; shif ++ )
			{
				funcX( A );
				funcY( A, B );
				funcZ( B, C, D, E );
			}
		}
	}
}

void funcX( int *A )
{
	// A を定義
}

void funcY( int *A, int *B )
{
	// A をベースに、B を作成
}

void funcZ( int *B, int *C, int *D, int *E )
{
	// B をベースに、C, D, E を作成
}
調べたところ、
配列を引数として渡すとき、実引数のコピーオブジェが作成されて...
それが膨大なスタックを消費して...
などとありました
しかし、sizeof( int ) = 4 byte で * 6000 = 24,000 byte で = 24 KB...

たとえコピーオブジェが作成されたとしても、なんら問題がないとは思うのですが。

[6000]程度の配列を引数として渡すことは問題なのでしょうか?
よろしくお願いします

Re:スタックオーバー

Posted: 2009年11月27日(金) 15:43
by 御津凪
handle_array 関数が再帰的に呼ばれている、
あるいは handle_array 関数のように膨大なサイズのローカル変数が定義されている関数を再帰的に呼び出しているのであればメモリが著しく消費されるのは容易に想像できますが、
少なくとも上記のコードからでは著しくメモリを消費される処理は見当たりません。
(処理中にメモリリークが発生している可能性もあります)

> [6000]程度の配列を引数として渡すことは問題なのでしょうか?

膨大なサイズの配列であっても、関数に渡す時は先頭要素へのアドレスのみを渡しているので、
この場合は今回の原因にはなっていないと思います。


タイトルにある「スタックオーバー」とは、スタックオーバーフローが発生したということでしょうか?
そうなのであれば関数を再帰呼び出ししている原因の可能性が高いのですが。

Re:スタックオーバー

Posted: 2009年11月27日(金) 16:08
by 初心者
>>御津凪 さま
 ご返事ありがとうございます
handle_array 関数を呼び出しているのは main ただ一つで他に無く、
handle_array 関数内で for していることもあり、呼び出しは 1 回きりで
再帰的に呼び出してはおりません

>>少なくとも上記のコードからでは著しくメモリを消費される処理は見当たりません。

そうですよね...
いつも td = 7 で重くなりだして、td = 8 でストップです。

>>膨大なサイズの配列であっても、関数に渡す時は先頭要素へのアドレスのみを渡しているので、

そうですよね...
しかし、御津凪 さまから見ても、[6000]は膨大ですか...
あちこちに「膨大」な配列を... とあっても、「膨大」を定義しているところはなかったので
疑問に思ってました。

>>タイトルにある「スタックオーバー」とは、スタックオーバーフローが発生したということでしょうか?

コンパイラが指摘したというものではなくて、メモリの増加からみて「スタックオーバーフロー」が起きているのではないか、と独自に推理しました。
OS : Vista Home;
CPU : Core 2 Duo;
Memory : 2GB;
使用メモリ量が 1.95GBで止まり、
int a; fscanf_s( "%d", a )で止まる時と同じメッセージでした。

なにはともあれ、
handle_array 関数内での構造的な問題ではなく、
また、配列を引数として渡すこと自体に問題はないことがわかりました。
あとは、funcX, Y, Z 内で問題がないか、index などを調べてみたいと思います。

ありがとうございます。

Re:スタックオーバー

Posted: 2009年11月27日(金) 16:11
by 初心者
訂正 : int a; scanf_s( "%d", a )で止まる時と同じメッセージでした。

Re:スタックオーバー

Posted: 2009年11月27日(金) 16:29
by softya
>コンパイラが指摘したというものではなくて、メモリの増加からみて「スタックオーバーフロー」が起きているのではないか、と独自に推理しました

それは勘違いです。
スレッドを大量に起動しない限りスタックを消費しても使用メモリは増えていきません。
スタックはプロセスやスレッド起動時に確保されてしまうからです。VC++の場合はデフォルトスタックのサイズは1MB程度でメモリを大量に消費するはずがありません。
徐々にメモリが増えていくのはnew演算子(C++)やmalloc()関数を使った場合です。

Re:スタックオーバー

Posted: 2009年11月27日(金) 16:37
by 御津凪
> しかし、御津凪 さまから見ても、[6000]は膨大ですか...
> あちこちに「膨大」な配列を... とあっても、「膨大」を定義しているところはなかったので
> 疑問に思ってました。

「膨大」な配列 かどうかのボーダーラインはプログラムによってさまざまなので、定義するものではありませんが、
単純なプログラムとしては 6000 個の int 型配列を5個スタック領域に確保するのは「膨大」かなと判断しただけです。

Re:スタックオーバー

Posted: 2009年11月27日(金) 16:50
by 初心者
>>softya さま
ご返事ありがとうございます。
実はもともと heap を使っていたのですが、
二週間前ぐらいに、heap では遅い (遅いと言われて)ということもあり、
stack で確保するように書き換えたのですが、
関数内部で、知らないうちに、malloc を呼び出している関数を使っているのかもしれません

>>徐々にメモリが増えていくのはnew演算子(C++)やmalloc()関数を使った場合です。

これで原因がはっきりしました。
徐々に増えていっていますから、(だいたい 0.03GB 毎)
malloc に違いありません。
勉強になりました。
ありがとうございます。

Re:スタックオーバー

Posted: 2009年11月27日(金) 17:24
by softya
えーと、大事な事が書かれていない事に気づきました。
>int a; fscanf_s( "%d", a )で止まる時と同じメッセージでした。
ってメモリ保護例外とかエラーメッセージが出たって事ですよね。
まず、出たエラーメッセージを正確に書いてください。
初心者さんがスタックオーバーフローと書いたため、回答者は無駄な検討をしちゃっている事になります。

VC++等のデバッガが一体化した開発環境を使っている場合はエラーが出たプログラムの行も分かるはずですので、それも教えてください。

Re:スタックオーバー

Posted: 2009年11月27日(金) 18:07
by 初心者
>>softya さま
遅れてすいません
出たエラーメッセージといいましても、明確なエラー内容があるわけではありません。
malloc 以外のメモリにアクセスしたときや、
FILE* f = NULL ,fopen_s のときのような、「あのウィンドウ」が出てくるわけではありません。

td = 8 の段階になると、中央に比較的小さなウィンドウで、

< Microsoft Windows >

...exe は動作を停止しました
この問題の解決策を確認しています...

問題が発生したため、プログラムが正しく動作しなくなりました。
プログラムは閉じられ、解決策がある場合は Windows から通知されます。

以上のようなウィンドウが出てきます。
これは、
int a; scanf_s( "%d", a ); で数値を入力すると出てくるメッセージと同じだったので
書きました。こういうエラーをなんというのかわからず、表現があいまいであったこと、
お詫びいたします。
今、ご指摘いただいた malloc free をやっております。

Re:スタックオーバー

Posted: 2009年11月28日(土) 02:17
by 初心者
 いや~!
>>御津凪 さま
>>softya さま
ありがとうございました!!!!

メモリ使用量が増えません!!!

助かりました!!
ここで相談しなければ、
引数に配列を渡さないため、すべての関数をマクロ形式で書き直して...いや、逆にすべて heap に
書き直して... いや、いったんファイルに保管して... などと考え途方にくれていました。

malloc 関連ファイルをすべて削除したらうまくいきました。
しかし、いまさらですが、heap は怖いですね。