- ベストアンサー
構造体メンバの個数
気になって質問させていただいたのですが、構造体のメンバが例えば10個とかあったりしたら、何かプログラムに異常が起きたりするものでしょうか? 現在作っているプログラム中で、本来入っているはずの数値(文字列)が、微妙にでたらめなものに変わってしまいます。(実際に起きたのが、int aの値は10なのに、46543とか。char name[20] = "ジタン" が、"ジタnp"とか) たまに、こういったことがプログラムを組んでいると起こるので、何が起こっているのか分かる方、御教授お願いします。
- みんなの回答 (26)
- 専門家の回答
質問者が選んだベストアンサー
後編: // 味方の攻撃番号を取得 static int turnInput( int check[] ) { int turn; for ( ; ; ){ printf( "誰が攻撃しますか(1.人物A 2.人物B 3.人物C 4.人物D)--->" ); scanf( "%d", &turn ); // 1~4以外ならプログラムの終了 if ( (turn < 1) || (turn > MAX_MEMBER) ){ exit( 1 ); // 強制終了 } // 0~3に変換 turn--; // 戦闘不能のチェック if ( Member[turn].hp <= 0 ){ printf( "戦闘不能です。\n" ); continue; } // 攻撃済みのチェック if ( check[turn] ){ printf( "/*-----%sのターンは終了しています-----*/\n", Member[turn].name ); continue; } // 攻撃番号を返す check[ turn ] = 1; // 攻撃済みフラグのセット printf( "\n" ); return turn; } } // 味方の攻撃ルーチン extern int attackMember( int members ) { int check[ MAX_MEMBER ] = { 0 }; int loop; dispStatus(); for ( loop = 1 ; loop <= members ; loop++ ){ printf( "味方の攻撃ターン(%d/%d)\n", loop, members ); switch ( turnInput(check) ){ case 0: chaAB( &Member[0], &Enemy[0] ); printf( "/*-----------------人物Aのターン終了------------------*/\n\n" ); break; case 1: chaAB( &Member[1], &Enemy[0] ); printf( "/*-----------------人物Bのターン終了------------------*/\n\n" ); break; case 2: chaCD( &Member[2], &Enemy[0] ); printf( "/*-----------------人物Cのターン終了------------------*/\n\n" ); break; case 3: chaCD( &Member[3], &Enemy[0] ); printf( "/*-----------------人物Dのターン終了------------------*/\n\n" ); break; default: loop = members; // 抜ける設定 break; } } return checkEnemy(); // 1:生きている 0:死す } // 敵の攻撃ルーチン extern int attackEnemy( void ) { int i, n1, s1, s2, s3, s4; dispStatus(); printf( "敵の攻撃ターン\n" ); for ( i = 0 ; i < MAX_MEMBER ; i++ ){ status *p = &Member[i]; srand( (unsigned)time(NULL) + rand() ); n1 = RAND( 1000, 1000 ); // ダメージ量(1000~1999) s1 = RAND( 100, 1 ); // [毒]状態(1-100) s2 = RAND( 100, 1 ); // [沈黙]状態(1-100) s3 = RAND( 100, 1 ); // [暗闇]状態(1-100) s4 = RAND( 100, 1 ); // [混乱]状態(1-100) if ( (p->hp -= n1) < 0 ){ p->hp = 0; } p->st[ 0 ] |= s1 % 2; // [毒]状態の設定 p->st[ 1 ] |= s2 % 2; // [沈黙]状態の設定 p->st[ 2 ] |= s3 % 2; // [暗闇]状態の設定 p->st[ 3 ] |= s4 % 2; // [混乱]状態の設定 printf( "%sの攻撃--->%sに%dのダメージを与えた。\n", Enemy[0].name, p->name, n1 ); } printf( "\n" ); return checkMember(); // 1:生きている 0:全滅 } // 人物A/Bの攻撃(勇者,戦士) extern void chaAB( status *member, status *enemy ) { int n1; // 初期化 srand( (unsigned)time(NULL) + rand() ); n1 = RAND( 1000, 1000 ); // ダメージ量(1000~1999) if ( (enemy->hp -= n1) < 0 ){ enemy->hp = 0; } printf( "%sの攻撃--->%sに%dのダメージを与えた。\n\n", member->name, enemy->name, n1 ); } // 人物C/Dの攻撃(魔法使い) extern void chaCD( status *member, status *enemy ) { int n1, select; // 初期化 srand( (unsigned)time(NULL) + rand() ); do { printf( "1.魔法1 2.魔法2--->" ); scanf( "%d", &select ); switch ( select ){ case 1: if ( member->mp < 20 ){ printf( "MPが足りません。\n" ); select = -1; break; } n1 = RAND( 1000, 1000 ); // ダメージ量(1000~1999) if ( (enemy->hp -= n1) < 0 ){ enemy->hp = 0; } member->mp -= 20; printf( "%sの魔法攻撃--->%sに%dのダメージを与えた。\n\n", member->name, enemy->name, n1 ); break; case 2: n1 = RAND( 50, 50 ); // MP吸収量(50~99) if ( enemy->mp < n1 ){ n1 = enemy->mp; } member->mp += n1; enemy->mp -= n1; printf( "%sの魔法吸収--->%sから%dのMPを吸収した。\n\n", member->name, enemy->name, n1 ); break; default: select = -1; break; } } while ( select == -1 ); } 以上。
その他の回答 (25)
- zwi
- ベストアンサー率56% (730/1282)
あっ書きもれました。 unsigned char *pDump = (unsigned char*)dt; にしてください。
補足
ごめんなさい。私そのダンプっていうのが良く分からないんで・・・ 取り合えずこんなのをを表示すればいいのですか? --- dump dt[5] struct --- 83 57 83 5E 83 93 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 70 17 00 00 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 83 72 83 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A0 0F 00 00 2C 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 83 4B 81 5B 83 6C 83 62 83 67 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A0 0F 00 00 FA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 83 58 83 5E 83 43 83 69 81 5B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 64 19 00 00 C8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 89 69 89 93 82 CC 88 C5 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A0 86 01 00 7A 1E 00 00 00 00 00 00 1B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- zwi
- ベストアンサー率56% (730/1282)
色々プログラムの修正が入ってますが、まだ再現する場合があるでしょうか? まだ異常があるなら、jactaさんが書かれていますが、正常な時と異常な時の16進ダンプが見たいですね。 こんなプログラムでお願いします。私の趣味でナル文字後だけでなく構造体全体のダンプをします。 unsigned char *pDump; printf( "--- dump dt[5] struct ---\n" ); for( int i=0 ; i<sizeof(dt) ; i++,pDump++ ) { printf( "%02X ", *pDump ); if( (i&7)==7 ) printf("\n"); }
- jacta
- ベストアンサー率26% (845/3158)
> st[4]は、st_ab[4]の間違いでした。 これもそうですし、他にも転記する際に省略されたり、転記ミスがあったりしますね。 字下げは気にしなくてもよいので、実際に使用しているものをそのままコピー&ペーストしてください。 また、 > その場合、nameのナル文字より後がどうなるかも教えてください。 これも重要な手がかりになるので、補足してください。
補足
戦闘場面のソースを載せます。 /*--------------main.c----------------*/ #include<stdio.h> #include<stdlib.h> #include<time.h> #include<windows.h> #include "myhead.h" int flg2 = 4; int main() { status dt[5] = {{"人物A",6000,100,{0,0,0,0}},{"人物B",4000,300,{0,0,0,0}}, {"人物C",4000,250,{0,0,0,0}},{"人物D",6500,200,{0,0,0,0}},{"human",100000,9999,{0,0,0,0}}}; int i=0,j,flg=0,turn,check[4] = {0}; int n1,n2,n3; //乱数専用 int stflg = 0; //状態異常判定用 srand((unsigned)time(NULL)); n1 = (rand() % 10) + 1; //最初の一回だけ。味方or敵からの攻撃を決める printf("/*-------------戦闘開始!!-------------*/\n"); if(n1 % 2 == 0){ while(1){ //(1) for(j=0;j<4;j++) if(dt[j].hp == 0) flg2--;//死んだキャラがいたらflg2--。 printf("/*-----------味方のステータス---------*/\n"); for(j=0;j<4;j++){ printf("%-10s: HP = %5d MP = %3d 状態 :",dt[j].name,dt[j].hp,dt[j].mp); if(dt[j].st_ab[0] == 1){ printf("毒 "); stflg = 1;} if(dt[j].st_ab[1] == 1){ printf("沈黙 "); stflg = 1;} if(dt[j].st_ab[2] == 1){ printf("暗闇 "); stflg = 1;} if(dt[j].st_ab[3] == 1){ printf("混乱 "); stflg = 1;} if(stflg == 0) printf("なし"); printf("\n"); stflg = 0; } printf("%-10s: HP = %5d\n",dt[4].name,dt[4].hp); printf("/*------------------------------------*/\n"); while(i < flg2){ //(2) if(dt[4].hp == 0) break; //humanのHPが0ならbreak; //(2)抜け printf("味方の攻撃ターン\n"); while(1){ //(3) while(1){ //(4) printf("誰が攻撃しますか(1.人物A 2.人物B 3.人物C 4.人物D)--->"); scanf("%d",&turn); if(dt[turn-1].hp <= 0) printf("戦闘不能です\n"); else break; //(4)抜け } printf("\n"); if(turn < 1 || turn > 4){ goto end; }else{ while(1){ //(5) for(j=0;j<i+1;j++){ if(check[j] == turn){ printf("/*-----%sのターンは終了しています-----*/\n",dt[turn-1].name); flg = 1; break; } } if(flg == 1) break; //(5)抜け else{ check[i++] = turn; if(turn == 1){ chaA(dt); printf("/*-----------------人物Aのターン終了-----------------*/\n\n"); }else if(turn == 2){ chaB(dt); printf("/*------------------人物Bのターン終了------------------*/\n\n"); }else if(turn == 3){ chaC(dt); printf("/*-------------人物Cのターン終了-------------*/\n\n"); }else if(turn == 4){ chaD(dt);printf("/*----------------人物Dのターン終了------------------*/\n\n");} flg = 2; break; //(5)抜け } } } if(flg == 2) break; //(3)抜け flg = 0; //これがないと正常動作しない } } if(dt[4].hp == 0){ printf("humanを倒しました!!\n"); break; //(1)抜け } human(dt); //4人攻撃が終わったので、次は敵のターン if(dt[0].hp == 0 && dt[1].hp == 0 && dt[2].hp == 0 && dt[3].hp == 0){ printf("/*----------ゲームオーバーです----------*/\n"); break; //味方全員戦闘不能 //(1)抜け } for(i=0;i<4;i++) check[i] = -1; i = flg = 0; flg2 = 4; Sleep(2000); system("cls"); } ここからはelseで、n1が奇数の場合、敵からの攻撃になるため攻撃順序がhuman関数から始まり、味方のターンになるため、主なソースは同じになってます。endは、main.cの一番最後にあります。 本題ですが、nameのNULL文字以降調べようとしたのですが、調べ方が分からなくて;; 基本的に文字化けしたり、値か変わったりするのは、ダメージを受けたあとになっているようです。
- Oh-Orange
- ベストアンサー率63% (854/1345)
★ワーニング出ませんでしたか? >printf("%p %p %p %p\n",dt[0].name,dt[0].HP,dt[0].MP,dt[0].st_ab[0]); >printf("%p %p %p %p\n",dt[1].name,dt[1].HP,dt[1].MP,dt[1].st_ab[0]); >printf("%p %p %p %p\n",dt[2].name,dt[2].HP,dt[2].MP,dt[2].st_ab[0]); >printf("%p %p %p %p\n",dt[3].name,dt[3].HP,dt[3].MP,dt[3].st_ab[0]); >printf("%p %p %p %p\n",dt[4].name,dt[4].HP,dt[4].MP,dt[4].st_ab[0]); ↑ ポインタ値を表示したいのなら HP、MPなどに『&』演算子をつけて下さい。 例えば printf( "%p %p %p %p\n", dt[0].name, &dt[0].HP, &dt[0].MP, &dt[0].st_ab[0] ); とします。もう一度正しくポインタ値を表示してみて下さい。 ・以上。→オプション設定でワーニングを一番高いレベルに設定しておきましょう。
お礼
本当ミスばっかりで申し訳ありません(汗) &をつけて表示させましたところ、ちゃんと表示されました。 0041014C 00410168 0041016C 00410170 00410174 00410184 004101A0 004101A4 004101A8 004101AC 004101BC 004101D8 004101DC 004101E0 004101E4 004101F4 00410210 00410214 00410218 0041021C 0041022C 00410248 0041024C 00410250 00410254
- Oh-Orange
- ベストアンサー率63% (854/1345)
★追記。 ・人物C,Dの処理で ans が 2 の処理が >(p+2)->mp += n1; >(p+4)->mp -= n1; となっています。ただしくは (p+2)->hp += n1; ←これ HP では。 (p+4)->mp -= n1; 細かい箇所を見直してみて下さい。 上記のは ans が 2 ならHPを回復する魔法なのですよね。 魔法でMP回復っておかしいので。 ・以上。
補足
非常に親切なアドバイス、ありがとうございます。 御指摘された、hpの0以下の処理は、4000文字を考慮してわざと書きませんでした。誤解させてすいません(汗)。 アドバイスいただいたやり方の方が、分かり易そうですね!やってみます。 下記のコメントですが↓ あと human、chaA、chaB、chaC、chaD の関数の初期化を関数で初期化して~ この関数の初期化を関数で初期化してっていうのはどういう意味でしょうか?私の日本語の取り違いかも知れませんが、教えていただけると幸いですm(_ _)m
- Oh-Orange
- ベストアンサー率63% (854/1345)
★アドバイス >作っているのは簡単な戦闘シュミレーションのプログラムです。 ↑ RPG ゲームかと思いました。 戦闘シュミレーションですか。了解。 次の点を確認して下さい。 ・『human.c』の human() 関数の >(p+i)->hp -= n2; ↑ 『n2』は宣言されていません。 仮に n1 だとしても hp が 0 以下になったとき 0 にする補正処理がありません。 れれだと hp=100 で n1=1500 なら hp-n1 を計算して -1400 になります。 他の場所も同じように 0 以下の補正処理がないよ。 ・あと human、chaA、chaB、chaC、chaD の関数の初期化を関数で初期化しても 同じになるのか調査して下さい。 初期化した直後の構造体データはすべて正しくセットされているですか? main.c のどこで定義しているのか分かりませんが次のように static status dt[] = { "人物A", 5000, 200, {0,0,0,0}, "人物B", 3000, 300, {0,0,0,0}, : "human", 9999, 999, {0,0,0,0}, }; ↑ この順番ですよね。→人物A、人物B、人物C、人物D、human >(1)dtを初期化しているのに、魔法を使おうとすると、mpが足りないと出る ↑ 正しく 人物A ⇒dt[0] 人物B ⇒dt[1] 人物C ⇒dt[2] 人物D ⇒dt[3] human ⇒dt[4] というアクセスをしていない気がします。 >(2)お互いにダメージを与えているのに敵だけhpが減らない ↑ これも同じで、構造体が人物名と正しく対応していないのでは? ・上手く対応させるには関数を全面的に作る変えた方が良いですよ。 つまり void human( status *enemy, status human[], int maxHuman ); //敵の攻撃関数 void chaA( status *enemy, status *chaA ); //味方、人物Aの攻撃 void chaB( status *enemy, status *chaB ); //味方、人物Bの攻撃 void chaC( status *enemy, status *chaC ); //味方、人物Cの攻撃 void chaD( status *enemy, status *chaD ); //味方、人物Dの攻撃 このようにします。このようにすれば各関数で 『(p+2)->mp』とか、『(p+4)->hp』とかしないで 『chaA->mp』とか、『enemy->hp』とかの指定で分かりやすくなります。 このようにしておかないと人物(A,B,C,D)と構造体(dt[0],dt[1],dt[2],dt[3])などが 何処かでずれてしまうかもしれない。また分かりにくかったりします。 ・それから構造体配列のアクセスで『(p+2)->mp』というのは『p[2].mp』としても アクセス可能です。私はこちらの方が分かりやすいと思います。→好みによるが…。 ・まずは main() 部分と chaA ~ chaD 関数を整理すべきだと思います。 すべての関数(chaA~chaD)は 第1引数が攻撃対象の構造体 status、 第2引数が人物(自分)の構造体 status と決めておきます。 あとは main() 関数の部分で構造体 dt[0]~dt[3] を渡すだけ。 この方がプログラミングしやすいですよ。 ・以上。いろいろと参考にして下さい。構造体がずれている気がしますので関数の整理を。
- zwi
- ベストアンサー率56% (730/1282)
ざっと、見る限りあやしい点はありません。 が、プログラムすべてが同じ構造体の定義を見ている場合に限りますが。 これ↓ typedef srtuct Status{ char name[256]; int hp,mp,st[4]; //st[4]は、状態異常を判定する配列 }status; と同じような構造体は定義していませんよね?st[]とst_ab[]で2種類の名前があったので気になりました。 これが問題なければ、どこかでp++をしているとか、st[4~]のアクセスをしている所があるぐらいしか思いつきません。
補足
す、すいません(汗) st[4]は、st_ab[4]の間違いでした。
- mikaemi
- ベストアンサー率50% (33/65)
専門家が見てくれると言ってくれているんだから、素直にべったり貼り付けるのがお得だと思いますよ^^
- jacta
- ベストアンサー率26% (845/3158)
この類の不具合は、コンパイルできて、かつ不具合が再現するソースを補足してもらわない限り原因が究明できる可能性は低いと思ってください。 また、どんな処理系を使っているのかも必須の情報です。 一度、char name[20]のところを、char name[256]ぐらいにして現象を確認してみてください。そして、その結果を教えてください。 その場合、nameのナル文字より後がどうなるかも教えてください。 (破壊がどこまで及んでいるかを知るためです)
お礼
こんなことろに書いてすいません。補足にて足りないことがあったので。 開発環境は、 エディタ : bcpad コンパイラ : borland C++ Compiler 5.5 (OS : Windows XP Professional) name[20]をname[256]に変えたところ、文字化けはしなくなりました。 しかし、name[20]に戻しても文字化けしなくなりました(汗) また、メモリ破壊が起きていると指摘されたので、初期化した後にメモリ番地を表示させてみたところ、 printf("%p %p %p %p\n",dt[0].name,dt[0].HP,dt[0].MP,dt[0].st_ab[0]); printf("%p %p %p %p\n",dt[1].name,dt[1].HP,dt[1].MP,dt[1].st_ab[0]); printf("%p %p %p %p\n",dt[2].name,dt[2].HP,dt[2].MP,dt[2].st_ab[0]); printf("%p %p %p %p\n",dt[3].name,dt[3].HP,dt[3].MP,dt[3].st_ab[0]); printf("%p %p %p %p\n",dt[4].name,dt[4].HP,dt[4].MP,dt[4].st_ab[0]); 結果: 0013F9F8 00001770 00000064 00000000 0013FB14 00000FA0 0000012C 00000000 0013FC30 00000FA0 000000FA 00000000 0013FD4C 00001964 000000C8 00000000 0013FE68 000186A0 0000270F 00000000 一応必要であれば。
補足
補足要求の皆さん、遅れてすいませんでしたm(_ _)m 言ってしまえば、作っているのは簡単な戦闘シュミレーションのプログラムです。攻撃して敵のHPを減らす。魔法を使えば自分のMPが減る。敵から攻撃を喰らえばHPが減り、状態異常を起こす。これを味方、敵(敵、味方)の順に交互に繰り返し、HPがなくなったら終了。至って普通だと思います。 前のから、多少変更がありますがご了承下さい。 /*------myhead.h------*/ typedef srtuct Status{ char name[256]; int hp,mp,st[4]; //st[4]は、状態異常を判定する配列 }status; void human(status *p); void chaA(status *p); void chaB(status *p); void chaC(status *p); void chaD(status *p); /*------main.c------*/ status dt[5] = {{"人物A",5000,200,{0,0,0,0},{"人物B",3000,300,{0,0,0,0}},~{"human",9999,999,{0,0,0,0}}}; int i,j; for(i=0;i<5;i++){ printf("%-10s %d %d",dt[i].name,dt[i].hp,dt[i].mp); for(j=0;j<4;j++) printf("%d ",dt[i].st[j]); printf("\n"); } human(dt); //敵の攻撃関数 chaA(dt); //味方、人物Aの攻撃 chaB(dt); //味方、人物Bの攻撃 chaC(dt); //味方、人物Cの攻撃 chaD(dt); //味方、人物Dの攻撃 for(i=0;i<5;i++){ printf("%-10s %d %d",dt[i].name,dt[i].hp,dt[i].mp); for(j=0;j<4;j++) printf("%d ",dt[i].st[j]); printf("\n"); } //この表示するときに、値がでたらめになっていることがある /*------human.c------*/ void human(status *p){ int i,j,n1; int s1,s2,s3,s4; srand((unsigned)time(NULL)); printf("敵の攻撃\n"); n1 = (rand() % 1000) + 1000; //ダメージ量 s1 = (rand() % 100) + 1; //状態異常を発生させる為の準備 s2 = (rand() % 100) + 1; s3 = (rand() % 100) + 1; s4 = (rand() % 100) + 1; for(i=0;i<4;i++){ (p+i)->hp -= n2; (p+i)->st_ab[0] = s1 % 2; //出来た乱数を2で割った余りを代入 ~~~~~~; (p+i)->st_ab[3] = s4 % 2; } /*------chaA(status *p)(chaB(status *p))------*/ /*人物A,Bは同じことをやっている*/ int n1; srand((unsigned)time(NULL)); n1 = (rand() % 1000) + 1000; (p+4)->hp -= n1; /*------chaC(status *p)------*/ /*人物C,Dは同じことをやっている*/ int n1,ans; srand((unsigned)time(NULL)); onemore: printf("1.魔法1 2.魔法2--->"); scanf("%d",&ans); if(ans == 1){ n1 = (rand() % 1000) + 1000; if((p+2)->mp < 20){ //chaDなら(p+3)になる printf("mpが足りません\n"); goto onemore; } (p+2)->mp -= 20; (p+4)->hp -= n1; }else if(ans == 2){ n1 = (rand() % 50) + 50; (p+2)->mp += n1; (p+4)->mp -= n1; } 長くなってしまって本当に申し訳ありません。状態異常のときにどうなるとかはまだ作っていませんが、ここまでで、おかしくなるのは (1)dtを初期化しているのに、魔法を使おうとすると、mpが足りないと出る (2)お互いにダメージを与えているのに敵だけhpが減らない 以上、補足とさせていただきます。
- Oh-Orange
- ベストアンサー率63% (854/1345)
★私もhuman関数の中身が見たいです。 あと >void human(status *p); は void human(Status *p); だよね。 ・ソースを貼り付けてみて下さい。
お礼
わざわざ考えていただいてありがとうございますm(_ _)m これを参考に、改良できる部分はしていきたいと思います。 いきなり直ったので、出来る限り原因追求していきます。