■ スタックオーバーフローにならいようにするためには?

[Visual C++ Q & A 掲示板] [過去ログの一覧]


vein 2008/01/22(火) 01:49:45 <初心者>
TCP送受信のプログラムを作成しているところですが、
スタックオーバーフローが出てしまいます。

MS visualStuio2003の出力は以下のとおりです。OSはXP pro SP1です。

TEST.exe!_chkstk()  行 91 Asm
TEST.exe!_NMSG_WRITE(int rterrnum=0x00000019)  行 194 + 0x18
TEST.exe!_amsg_exit(int rterrnum=0x00000019)  行 320 + 0x9
TEST.exe!_purecall()  行 53 + 0x7 C
TEST.exe!std::_Destroy<RecvDataBuff>(RecvDataBuff * _Ptr=0x00abcb10)  行 49 + 0xf C++
  TEST.exe!std::allocator<RecvDataBuff>::destroy(RecvDataBuff * _Ptr=0x00abcb10)  行 152 + 0x9 C++
  TEST.exe!std::deque<RecvDataBuff,std::allocator<RecvDataBuff> >::pop_front()  行 575 C++
  TEST.exe!std::queue<RecvDataBuff,std::deque<RecvDataBuff,std::allocator<RecvDataBuff> > >::pop()  行 72 C++
  TEST.exe!CSockTcp::GetRecvData()  行 260 C++

CByteArray& CSockTcp::GetRecvData()
{
m_recv.Copy( m_queue.front() );
m_queue.pop();
return m_recv;
}

  TEST.exe!CXXXDlg::RecvData(PEER_KIND peer=PEER_CTL2, long size=0x0000000c)  行 656 + 0x12 C++

// 受信データ取得
☆m_recvBuf.Copy( m_sockObj[peer]->GetRecvData() );☆


原因は以下のエラーだとおもうのですが、なんでこれが出るのかがわからないのです。
rterrno=0x00000019 rterrtxt=0x00624c90 R6025 pure virtual function call

virtual function なんて呼び出してないよ><

スタックオーバーフローは関数を呼び出しすぎると起きるそうですが、
送受信のためにぐるぐる回していても問題ないですよね?

情報が足りないかもしれませんが、コードの抜粋を載せます。
(エラーのところも少し載せてますが)

class RecvDataBuff : public CByteArray
{
public:
RecvDataBuff();
RecvDataBuff(const RecvDataBuff& src);
~RecvDataBuff();
};

ソケットは、送受信用にサーバーが3個、クライアントを1個使用しています。

受信処理は以下のような感じでヘッダのサイズ情報からメッセージ分だけ受信するようにしています。
bool CSockServer::RecvData()
{
struct MIF_0001 hed = {0};
long     rtn;
LPBYTE     ptr = (LPBYTE)&hed;
long     size = sizeof(struct MIF_0001);
RecvDataBuff   tmp;
long         total;

// ヘッダ部受信
while( size > 0 )
{
rtn = Recv( ptr, size );
if ( rtn < 0 && rtn != SS_TIMEOUT )
{
CloseSocket();
return false;
}

if ( rtn == SS_TIMEOUT )
{
return true;
}
ptr += rtn;
size -= rtn;
}
// 総サイズ
// total = ntohs(hed.msg_size) + sizeof(hed);
total = ntohs(hed.msg_size);
tmp.SetSize( total );
// ヘッダサイズを除くサイズ
size = total - sizeof(struct MIF_0001);
if ( size > 0 )
{
ptr = tmp.GetData();
ptr += sizeof(struct MIF_0001);
RecvData( ptr, size );
}

memcpy( tmp.GetData(), &hed, sizeof(struct MIF_0001) );

m_queue.push( tmp );

::PostMessage( m_hWnd, WM_SS_RECV, m_threadNum, total );

return true;
}

補足:queue<RecvDataBuff> m_queue;

えてこう 2008/01/22(火) 16:20:10
スタックオーバーフローではないのでは?

http://support.microsoft.com/kb/125749/ja
http://msdn2.microsoft.com/ja-jp/library/wbddte9e(VS.80).aspx

この辺りのが参考になるかも...

2008/01/22(火) 19:00:45
ちなみにスタックオーバーフローの時は「stack overflow」とはっきり出ますが、それは出てます?

vein 2008/01/23(水) 01:05:18 <初心者>
こんばんは、レスありがとうございます。

stack overflowはちゃんと?出ています。
初回の例外発生〜

ブレークポイント挟みながら、デバックしていたら、R6025が発生し、F5(実行)を10回以上すると、スタックオーバーフローします。

ブレークポイントは_purecallに設定しました。

R6025のエラーが何度も出て、結果的にスタックオーバーフローになっているみたいです。

通信状態はまったく同じでも、出るときは数分〜4時間ぐらい出ないときもあります。

えてこうさんのページを見ましたけど、以下のようなことはしてないはずなんですけど。。。
>このエラーは、派生クラス型へのキャストによって作成されたポインタ (実際は基本クラスを指すポインタ) を使って抽象基本クラス中の仮想関数が呼び出されたときに発生します。また、基本クラスの構築時に生成された void* からクラスのポインタへのキャストが行われたときにも発生します。

2008/01/24(木) 18:22:27
スタックオーバーフローならスタックの使用を下げるしかありません。標準では1スレッド辺り1MBしかありませんので、それ以内に収まるようにローカル変数を減らしてください。再帰関数や複数のスレッドとして作成される関数はグローバルやstaticは使えませんからmallocやnewする必要があります。

>スタックオーバーフローは関数を呼び出しすぎると起きるそうですが、
「再帰関数」と「ある関数を複数のスレッドとして作成する」ことは違います。

>送受信のためにぐるぐる回していても問題ないですよね?
受信待ちの無限ループのことなら上記の話とも違います。色々混同してません?

シャノン 2008/01/24(木) 19:07:30 <常連>
というか、pure virtual function call が起こるのがそもそもおかしいです。
まずはそっちをなんとかしましょう。
RecvDataBuff のデストラクタあたりが怪しい気がします。

rin 2008/01/26(土) 00:32:19
>bool CSockServer::RecvData()

>rtn = Recv( ptr, size );

>RecvData( ptr, size );
このあたりに疑問を感じるのだけど

2008/01/26(土) 01:33:10
ああ、ほんとだw意味の無い再帰になってる。

vein 2008/01/28(月) 21:12:05 <初心者>
すみません、モデムが壊れて書き込めませんでした。

原因はpure virtual function call がなんども起きているからだと思います。これを直せばオーバーフローはしないと思うのですが、
自作のソースには、純粋仮想関数とかは定義していないので、なぜ発生するかはまだわかっていません。

>RecvDataBuff のデストラクタあたりが怪しい気がします。
ソースは以下のとおりです。多分問題ないと
RecvDataBuff::RecvDataBuff() : CByteArray()
{
}
RecvDataBuff::RecvDataBuff(const RecvDataBuff& src)

this->RemoveAll();
this->Copy(src);
}
RecvDataBuff::~RecvDataBuff()
{
}

以下については、関数が違うので、再帰ではないです。
情報不足で申し訳ありません。
>bool CSockServer::RecvData()
>rtn = Recv( ptr, size );
>RecvData( ptr, size );

ソケットのクラスは
CSockTcp
CSockServer:CSockTcp
があります。
受信スレッド
Thread()
{

CSockServer::RecvData();

}

先に呼ばれる受信関数
bool CSockServer::RecvData()
{
struct MIF_0001 hed = {0};
long rtn;
LPBYTE ptr = (LPBYTE)&hed;
long size = sizeof(struct MIF_0001);
RecvDataBuff tmp;
long total;

// ヘッダ部受信
while( size > 0 )
{
rtn = Recv( ptr, size );
if ( rtn < 0 && rtn != SS_TIMEOUT )
{
CloseSocket();
return false;
}

if ( rtn == SS_TIMEOUT )
{
return true;
}
ptr += rtn;
size -= rtn;
}
// 総サイズ
// total = ntohs(hed.msg_size) + sizeof(hed);
total = ntohs(hed.msg_size);
tmp.SetSize( total );
// ヘッダサイズを除くサイズ
size = total - sizeof(struct MIF_0001);
if ( size > 0 )
{
ptr = tmp.GetData();
ptr += sizeof(struct MIF_0001);
RecvData( ptr, size );
}

memcpy( tmp.GetData(), &hed, sizeof(struct MIF_0001) );

m_queue.push( tmp );

::PostMessage( m_hWnd, WM_SS_RECV, m_threadNum, total );

return true;
}

↑の関数から呼び出す受信関数
void CSockServer::RecvData( LPBYTE buf, long size )
{
long rtn;
LPBYTE ptr = buf;

// ヘッダ部受信
while( size > 0 )
{
rtn = Recv( ptr, size );
if ( rtn < 0 && rtn != SS_TIMEOUT )
{
CloseSocket();
return;
}

if ( rtn == SS_TIMEOUT )
{
return;
}
ptr += rtn;
size -= rtn;
}
}

赤猿 2008/01/30(水) 13:08:58 <中級者>
> Thread()
> {
> ・
> CSockServer::RecvData();
> ・
> }

RecvData()はこういう呼び出しをしているのでしょうか?

vein 2008/02/02(土) 13:36:08 <初心者>
呼び出し方は、正しくは、
↓のとおりです。(CSockServer* pServer;)
bRtn = pServer->RecvData();

デバック操作を教えてもらって、確かめたところ、
m_queueにpop不可能なデータが入り、以降からpopのとこでpopできずに、
R6025が発生します。

m_queueの中は以下のようになってました。(c-_Map-[CByteArray])
m_pData    0x00000000 <不適切なPtr>
m_nSize    0x00000000
m_nMaxSize 0x00000000
m_nGrowBy  0x00000000

なぜ、m_queueにpop不可なデータが入るかまでは分かりませんでした。

pushの前でデータが正常かどうかTRACEで出すようにしましたけど、異常なデータはなかったです。(要素が0になっていないかを見ました)

毎週金曜日はポイント最大3倍!さらに4倍のチャンスも!

Programming Library