プログラムについて
今スタっクのファイルから読み込んだ文字列をスタっクへプっシュしたりポっプしたりする過程がわかるプログラムを作ってます
。
作りかけのプログラムですがどこをどうすればいいか教えてください!!
一応コンパイルできます。
使ってない関数があるかもしれません。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUMBER 100
/*--- スタックを実現する構造体 ---*/
typedef struct {
int max; /* スタックのサイズ */
int ptr; /* スタックポインタ */
char *stk; /* スタック(の先頭要素へのポインタ) */
} Stack;
/*--- スタックの初期化 ---*/
int StackAlloc(Stack *s, int max)
{
s->ptr = 0;
if ((s->stk = calloc(max, sizeof(int))) == NULL)
{
s->max = 0; /* 配列の確保に失敗 */
return (-1);
}
s->max = max;
return (0);
}
/*--- スタックの後始末 ---*/
void StackFree(Stack *s)
{
if (s->stk != NULL) {
free(s->stk);
s->max = s->ptr = 0;
}
}
/*--- スタックにデータをプッシュ ---*/
int StackPush(Stack *s, int x, int i,char *a[])
{
if (s->ptr >= s->max)
return (-1);
if(x>=i)
{
puts("プッシュできる文字データはありません。");
return (-1);
}
strcpy(&s->stk[s->ptr++],&(*a)[x]);
x++;
return x;
}
/*--- スタックからデータをポップ ---*/
int StackPop(Stack *s, int x,char *a[])
{
if (s->ptr <= 0) /* スタックは空 */
return (-1);
strcpy(&s->stk[--s->ptr],&(*a)[x]);
return (0);
}
/*--- スタックからデータをピーク ---*/
int StackPeek(const Stack *s, int *x)
{
if (s->ptr <= 0) /* スタックは空 */
return (-1);
*x = s->stk[s->ptr - 1];
return (0);
}
/*--- スタックの大きさ ---*/
int StackSize(const Stack *s)
{
return (s->max);
}
/*--- スタックに積まれているデータ数 ---*/
int StackNo(const Stack *s)
{
return (s->ptr);
}
/*--- スタックは空か ---*/
int StackIsEmpty(const Stack *s)
{
return (s->ptr <= 0);
}
/*--- スタックは満杯か ---*/
int StackIsFull(const Stack *s)
{
return (s->ptr >= s->max);
}
/*--- スタックを空にする ---*/
void StackClear(Stack *s)
{
s->ptr = 0;
}
int main(void)
{
int i=0,j,ret,x=0;
char *a[NUMBER];
char buffer[20];
FILE *fpin;
Stack s;
fpin=fopen("input2.txt","r"); //テキストファイルを読み取りモードで開く
while(fgets(&buffer[0],sizeof(buffer),fpin) !=NULL )
{
if(i>=NUMBER)//読み込む人数がNUMBERを超えてる時の処理
{
puts("人数が100人を超えています");
goto END;
}
strcpy(&a[i][0],&buffer[0]);
/*ret=sscanf(&buffer[0],"%s",&a[i][0]); //データ文字列を3分割
if(ret!=1) //1に分割できなかったときの処理
{
puts("代入された入力項目の個数が3でありません");
goto END;
}*/
i++;
}
for(j=0; j<i; j++)
printf("%s\n",&(*a)[j]);
if (StackAlloc(&s, 100) == -1) {
puts("スタックの確保に失敗しました。");
return (1);
}
while (1) {
int m;
printf("現在のデータ数:%d/%d\n", StackNo(&s), StackSize(&s));
printf("(1) プッシュ (2) ポップ (0) 終了:");
scanf("%d", &m);
if (m == 0) break;
switch (m) {
case 1: printf("データ:"); puts("こんちくしょ~");
if(StackPush(&s, x,i,&(*a)) == -1)
puts("スタックへのプッシュに失敗しました。"); break;
case 2: if(StackPop(&s, x,&(*a)) == -1)
puts("ポップできません。");
else
printf("ポップしたデータは%sです。\n", &s.stk[s.ptr]); break;
}
}
StackFree(&s);
END:
fclose(fpin);
return (0);
}
お礼
どうもありがとうございます♪ なるほど、この場合だとループとcontinue文で出来そうですね。 ただ、このコードでは 実際やってみれば分かりますが namespace Debug { void f(int i); void f(const void* ); } class AAA : Super { //実験を楽にするためだけなので超適当 void Save( MemoryMap* ) const override {} public: AAA*& Next() const { return (AAA*&)next; } AAA*& Child() const { return (AAA*&)child; } int i; AAA(int a) : i(a){} ~AAA(){ Debug::f(i); } }; namespace Debug { //各状況チェック void f(int i){ TCHAR c[20]; _stprintf_s(c,20,_T("%d\r\n"),i); OutputDebugString(c); } void Debug::f( const Void* p ){ TCHAR c[100]; _stprintf_s(c,100,_T("%08p\r\n"),p); OutputDebugString(c); } } で、基底クラスのデストラクタは Super::~Super(){ static int num=0; Debug::f(++num); } にかえ AAA* base = new AAA(423); base->Next() = new AAA( 5464 ); base->Child() = new AAA( 5555 ); base->Next()->Child() = new AAA( 777 ); base->Next()->Next() = new AAA( 888 ); base->Next()->Child()->Next() = new AAA( 1111 ); Super::Release( &base ); Debug::f(base); としてみると 質問文で私が作った再帰版では 5555 1 423 2 777 3 1111 4 5464 5 888 6 00000000 となり、最後にbaseも自動的にNULLに変わって 解決ですが kmeeさんご提示のコードでは 5555 1 までで死にます。 原因は、NULLに直らないことによる2重deleteです。 私が作った再帰版ではポインタポインタを引数として渡しているので 超お手軽な表記になっていますが そこを考慮して簡易的に書きなおしてみると #include <stack> void Super::Release( PTR* pp ){ std::stack<PTR*> sp; sp.push(pp); for ( PTR p; !sp.empty(); ){ p = *sp.top(); if ( !p ){ sp.pop(); continue; } if ( p->child ){ sp.push( &p->child ); continue; } *sp.top() = p->next; delete p; } } こんなかんじですかね。 これなら 5555 1 423 2 777 3 1111 4 5464 5 888 6 00000000 で、期待どおりです。 (たぶんないとは思いますが、ややこしいコードなので、気づいてないとこでバグってる箇所があるかもしれませんw 見つかったら教えていただけるとありがたいです。) ただ 今まで書いたコードについて 全て当てはまりそうかどうか パフォーマンス、メンテなど含めgoto文とcontinue どっちが良いかなど まだ精査したいことがいくつかあるので しばらくお待ちください。
補足
追加報告です。 >原因は、NULLに直らないことによる2重deleteです。 あれ、正確にはdelete済みのやつのメンバへのアクセス違反でしたかね そこんとこ失礼しました。 (意味的に再帰になるコードは、どう組んでも分かり辛いですねw) 今のところ 親よりchildの方の処理を先に行う childより親の方の処理を先に行う の2パターンあり得るというだけで ノード操作関連ではcontinueとループを使った方法でも問題なさそうと分かりました。 ただ、パフォーマンス的には 10回程度やって平均が 0.002866971356706sec(stack&gotoなし) 対 0.001327178776805sec(再帰) ぐらいになってるような状況でした。 そこで、まだコードを出していないgotoだとどうなるかですが std::stackの微妙な仕様が若干煩わしかったので ポインタの出し入れのみと拡張を簡単に行う自作スタックを作ってやってみました。 (キャッシュの連続性を求めてポインタの配列を動的に確保するという形で、拡張が必要になったら拡張のみは行い、縮小はせず、デストラクタで一括解放) なお、書き忘れてましたが、プリコンパイル済みヘッダで typedef VOID Void; typedef size_t szt; typedef BOOL Bool; こんなことをやってます。 ///////SimpleStack.h////// #pragma once class SimpleStack { typedef Void* VP; VP* data; szt len, cur; const szt cycle; //再確保が必要な場合のサイクル(要素数) Void PushVP( VP ); bool PopVP( VP* pp ){ if (!cur) return false; *pp = data[--cur]; return true; } public: SimpleStack( szt cycle_ = 4 ); ~SimpleStack(); template < typename T > Void Push( T* p ){ PushVP( (VP)p ); } template < typename T > bool Pop( T** pp ){ return PopVP( (VP*)pp ); } }; ///////SimpleStack.cpp////// #include "StdAfx.h" #include "SimpleStack.h" SimpleStack::SimpleStack( szt cycle_ ) : len(0), cur(0), cycle(cycle_) {} SimpleStack::~SimpleStack(){ if (len) free( data ); } Void SimpleStack::PushVP( const VP p ){ if ( cur == len ){ const szt newlen = len + cycle; VP* t = (VP*)malloc( sizeof( VP ) * newlen ); if ( len ){ memcpy( t, data, sizeof(VP)*len ); free( data ); } data = t; len = newlen; } data[cur++] = p; } これで、Push(ポインタ);で押し込み Pop(&ポインタ);で拾いつつ、戻り値チェックでスタックが空かどうか分かります。 そうすると、gotoを使う場合、一例としてはこうなります。 Void Super::Release( PTR* pp ){ SimpleStack sp; PTR p; while ( p = *pp ){ if ( p->child ){ sp.Push(pp); pp = &p->child; continue; } RELEASED_ALL_CHILDREN: //ここに来る時にはpの子ノードは解放済み *pp = p->next; delete p; } if ( sp.Pop(&pp) ){ p = *pp; goto RELEASED_ALL_CHILDREN; } } なんと、コード量で言うとそんなには変わりがないって感じでした。 でもって、この場合やっぱりジャンプ箇所一つだけなので、フラグはなくていいですね。 そして、Push,Pop及びそれに伴うチェックがchildがらみの個所のみなので それほど可読性も低くないと思われます。 速度ですが 0.002866971356706sec(stack&gotoなし) 0.001327178776805sec(再帰) 0.001447426842494sec(stack&goto) こんなもんでした。 これは捨てがたいですw 自前のスタックでも 思ったよりも簡単かもしれない、という事が分かりましたが その他のアイデアや突っ込みどころがありましたら是非教えてください。