こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

構造体メンバの個数

気になって質問させていただいたのですが、構造体のメンバが例えば10個とかあったりしたら、何かプログラムに異常が起きたりするものでしょうか?

現在作っているプログラム中で、本来入っているはずの数値(文字列)が、微妙にでたらめなものに変わってしまいます。(実際に起きたのが、int aの値は10なのに、46543とか。char name[20] = "ジタン" が、"ジタnp"とか)

たまに、こういったことがプログラムを組んでいると起こるので、何が起こっているのか分かる方、御教授お願いします。

投稿日時 - 2007-08-27 14:57:21

QNo.3290894

困ってます

質問者が選んだベストアンサー

後編:

// 味方の攻撃番号を取得
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 );
}
以上。

投稿日時 - 2007-08-29 19:48:20

お礼

わざわざ考えていただいてありがとうございますm(_ _)m

これを参考に、改良できる部分はしていきたいと思います。
いきなり直ったので、出来る限り原因追求していきます。

投稿日時 - 2007-08-30 12:43:46

このQ&Aは役に立ちましたか?

11人が「このQ&Aが役に立った」と投票しています

回答(26)

ANo.25

★似たものを作ってみました。
>実は、No16さん以降、ソースを追加したり修正しているうちに、文字化けやバグが無くなりました(汗)
 ↑
 これ本当に直った?不思議ですね。たまたま文字化けしなくなった気が…。
・私も『戦闘シミュレーション』を作ってみました。→sikimori さんのソースを元に。
 main() 関数を複数のサブ関数に分割してみました。今後の参考にしてみて下さい。
 ※1つのソースで作成しました。→前編、後編で貼り付けておく。

前編:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

// 構造体
typedef struct Status {
 char name[ 20 ]; // キャラクタ名
 int hp;   // HP量
 int mp;   // MP量
 int st[ 4 ];  // 特殊状態(毒,沈黙,暗闇,混乱)
} status;

// 記号定数
#define MAX_ENEMY  (sizeof(Enemy) / sizeof(status))
#define MAX_MEMBER  (sizeof(Member) / sizeof(status))

// マクロ関数
#define RAND(max,ofs) ((rand() % (max)) + (ofs))

// 関数のプロトタイプ宣言
extern void dispStatus( void );
extern int checkEnemy( void );
extern int checkMember( void );
extern int checkGameOver( int *members );
extern int attackMember( int members );
extern int attackEnemy( void );
extern void chaAB( status *member, status *enemy );
extern void chaCD( status *member, status *enemy );

// 敵キャラのデータ
extern status Enemy[ 1 ] = {
 "human", 25000, 9999, 0, 0, 0, 0,
};

// 味方キャラのデータ
extern status Member[ 4 ] = {
 "人物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,
};

// 戦闘シミュレーション
int main( void )
{
 // 生き残り人数
 int members;
 // 初期化
 srand( (unsigned)time(NULL) );
 printf( "/*-------------戦闘開始!!-------------*/\n\n" );
 // 最初の1回だけ。味方or敵(0:先手,1:後手)
 if ( RAND(2,0) == 0 ){  // 味方が先手
  while ( !checkGameOver(&members) ){
   if ( attackMember(members) ){  // 味方の攻撃
    if ( attackEnemy() ){   // 敵の攻撃
     Sleep( 2000 );
     system( "cls" );
    }
   }
  }
 }
 else{      // 敵が先手
  while ( !checkGameOver(&members) ){
   if ( attackEnemy() ){    // 敵の攻撃
    if ( attackMember(members) ){ // 味方の攻撃
     Sleep( 2000 );
     system( "cls" );
    }
   }
  }
 }
 return 0;
}

// 敵,味方ステータスの表示
static void dispStatusOne( status *data )
{
 static const char *msg[] = {
  "毒",
  "沈黙",
  "暗闇",
  "混乱",
 }; int i, flag = 0;
 
 // 名前,HP,MPの表示
 printf( "%-10s: HP = %6d MP = %4d 状態 :", data->name, data->hp, data->mp );
 // 状態の表示
 if ( data->hp == 0 ){
  printf( "死亡\n" );
  return;
 }
 for ( i = 0 ; i < 4 ; i++ ){
  if ( data->st[i] ){
   if ( flag ){
    printf( " " );
   }
   printf( msg[i] ); // 毒,沈黙,暗闇,混乱の表示
   flag = 1;
  }
 }
 if ( flag == 0 ){
  printf( "なし" );
 }
 printf( "\n" );
}

// 敵,味方ステータスの表示
extern void dispStatus( void )
{
 int i;
 
 printf( "/*-----------味方のステータス---------*/\n" );
 for ( i = 0 ; i < MAX_MEMBER ; i++ ){
  dispStatusOne( Member + i );
 }
 printf( "/*-------------敵のステータス---------*/\n" );
 for ( i = 0 ; i < MAX_ENEMY ; i++ ){
  dispStatusOne( Enemy + i );
 }
 printf( "/*------------------------------------*/\n" );
 printf( "\n" );
}

// 敵のHPをチェック
extern int checkEnemy( void )
{
 return (Enemy[0].hp > 0) ? 1 : 0;  // 1:敵生きている 0:敵死す
}

//味方のHPをチェック
extern int checkMember( void )
{
 int i;
 
 for ( i = 0 ; i < MAX_MEMBER ; i++ ){
  if ( Member[i].hp > 0 ){
   return 1;
  }
 }
 return 0;  // 全滅
}

// ゲームオーバーのチェック
extern int checkGameOver( int *members )
{
 int i, count = 0;
 
 if ( checkEnemy() == 0 ){
  dispStatus();
  printf( "%sを倒しました!!\n", Enemy[0].name );
  return 1; // 勝利!
 }
 if ( checkMember() == 0 ){
  dispStatus();
  printf( "/*----------ゲームオーバーです----------*/\n" );
  return 1; // 全滅!
 }
 for ( i = 0 ; i < MAX_MEMBER ; i++ ){
  if ( Member[i].hp > 0 ){
   count++;
  }
 }
 *members = count;
 return 0;
}

投稿日時 - 2007-08-29 19:47:07

ANo.24

 enum human_t { Ahuman = 0, Bhuman, ..., Zhuman, humanNum };
か^^

投稿日時 - 2007-08-29 15:30:06

ANo.23

ちらっと眺めただけで、プログラムを読んでないし、デバッグと直接関係ないですが。。茶々入れてるだけかと思われるならゴメンナサイ^^;

数字をそのまま使うより、
 enum human_t { Ahuman, Bhuman, ... };
なんか定義して、列挙にしておいたほうがわかりやすいんじゃないですか?
配列の添え字として使う場合は、
 enum human_t { Ahuman = 0, Bhuman, ..., Zhuman, humanNum = Zhuman };
などとして、0 を明示しておいたり、長さを別に定義しておいたり。。

あと、C++コンパイラが使えて、C99の拡張機能を使っていないなら、C++コンパイラにかけてみるのもプログラムチェックに役立つと思いますよ(文字サイズなどちょっとした違いが CとC++にはありますが)。

投稿日時 - 2007-08-29 15:23:53

ANo.22

★訂正。
・check[] 配列のチェックは
 if ( check[turn-1] ){
  printf( "/*-----%sのターンは終了しています-----*/\n", dt[turn-1].name );
 }
 else switch ( turn ){
  case 1: chaAB( &dt[0], &dt[4] ); printf( … ); break;
  case 2: chaAB( &dt[1], &dt[4] ); printf( … ); break;
  case 3: chaCD( &dt[2], &dt[4] ); printf( … ); break;
  case 4: chaCD( &dt[3], &dt[4] ); printf( … ); break;
  default:break;
 }
 でした。
 for文で4回ifチェックしなくても良かった。
・以上。

投稿日時 - 2007-08-29 09:07:12

ANo.21

★いろいろと怪しい箇所(疑問)が見つかった。
・(1)for ( j = 0 ; j < 4 ; j++ ) if( dt[j].hp == 0 ) flg2--;//死んだキャラがいたらflg2--。
  この処理は
  flg2=4 してから (hp == 0) なら flg2-- よりも
  flg2=0 してから (hp > 0) なら flg2++ の方が分かりやすくないか。
  for ( flg2 = j = 0 ; j < 4 ; j++ ) if ( dt[j].hp > 0 ) flg2++;
 (2)変数 i と check[ i++ ] = turn; の部分がきっとずれるかも。
  ここのロジックが変なので chaA()~chaD() 関数が正しく呼ばれていないと思う。
  よって選択した人物とは違う人物関数が処理される。
  そして『MPが足りない』となる。
 (3)human() 関数で人物A~人物Dの4人に対して同じダメージで攻撃しています。
  この動作で良いのですか?
 (4)chaA()、chaB() が構造体データ以外はすべて同じなら1つの関数に出来ます。
  chaC()、chaD() も構造体データ以外はすべて同じなら1つの関数に出来ます。
  処理する構造体データの部分だけが違うのなら次の2つに出来ます。
  void chaAB( status *member, status *enemy );
  void chaCD( status *member, status *enemy );
  このような引数にすれば
  switch ( turn ){
   case 1: chaAB( &dt[0], &dt[4] ); printf( … ); break;
   case 2: chaAB( &dt[1], &dt[4] ); printf( … ); break;
   case 3: chaCD( &dt[2], &dt[4] ); printf( … ); break;
   case 4: chaCD( &dt[3], &dt[4] ); printf( … ); break;
   default:break;
  }
  と出来ます。
 (5)human() 関数で戦闘するたびに毒、沈黙、暗闇、混乱の状態がセット/リセットします。
  普通は一度『毒』状態になったら『毒消し』しない限りはずっと『毒』状態になる気が…。
  なので代入ではなくてビットORすれば良い。
  (p+i)->st_ab[0] |= s1 % 2;
  (p+i)->st_ab[1] |= s2 % 2;
  (p+i)->st_ab[2] |= s3 % 2;
  (p+i)->st_ab[3] |= s4 % 2;
  とします。→『|=』を使う。
・上記の5つ以外にも気になる点がゴロゴロあります。
 データは
 人物A~人物Dまでを status member[4]; として定義して
 humanという敵だけを status enemy[1]; として定義した方が分かりやすいですよ。
 また構造体データ member、enemy をグローバル変数にして main() 関数の処理を
 複数のサブ関数に分けたほうが見やすくなります。
 これによりバグになりにくくなる。
・下に main() 関数と分けたほうが良さそうな関数のプロトタイプ宣言を載せておきます。
 作り変えるときの参考にして下さい。

// 記号定数
#define MAX_ENEMY  (sizeof(Enemy) / sizeof(status))
#define MAX_MEMBER  (sizeof(Member) / sizeof(status))

// マクロ関数
#define RAND(max,ofs) ((rand() % (max)) + (ofs))

// 関数のプロトタイプ宣言
extern void dispStatus( void ); // ステータスの表示
extern int attackEnemy( void ); // 敵の攻撃(戻り値:checkEnemy()の値)
extern int attackMember( int members ); // 味方の攻撃(戻り値:checkMember()の値)
extern int checkEnemy( void ); // 敵のHP=0をチェック(1=生きている,0=倒れた)
extern int checkMember( void ); // 味方4人のHP=0をチェック(1=戦える,0=全滅)
extern int checkGameOver( int *members ); // ゲームオーバーをチェック(1=GameOver,0=戦闘続行)
extern void chaA( status *member, status *enemy ); // 人物Aの攻撃
extern void chaB( status *member, status *enemy ); // 人物Bの攻撃
extern void chaC( status *member, status *enemy ); // 人物Cの攻撃
extern void chaD( status *member, status *enemy ); // 人物Dの攻撃

// 敵キャラのデータ
extern status Enemy[ 1 ] = {
 "human", 100000, 9999, 0, 0, 0, 0,
};

// 味方キャラのデータ
extern status Member[ 4 ] = {
 "人物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,
};

// 戦闘シミュレーション
int main( void )
{
 // 生き残り人数
 int members;
 // 初期化
 srand( (unsigned)time(NULL) );
 printf( "/*-------------戦闘開始!!-------------*/\n" );
 // 最初の1回だけ。味方or敵(0:先手,1:後手)
 if ( RAND(2,0) == 0 ){  // 味方が先手
  while ( !checkGameOver(&members) ){
   dispStatus();      // ステータスの表示
   if ( attackMember(members) ){  // 味方の攻撃
    if ( attackEnemy() ){   // 敵の攻撃
     Sleep( 2000 );
     system( "cls" );
    }
   }
  }
 }
 else{      // 敵が先手
  while ( !checkGameOver(&members) ){
   dispStatus();      // ステータスの表示
   if ( attackEnemy() ){    // 敵の攻撃
    if ( attackMember(members) ){ // 味方の攻撃
     Sleep( 2000 );
     system( "cls" );
    }
   }
  }
 }
 return 0;
}

最後に:
・今回の原因は check[] 配列のロジックが正しくないようです。
 『check[ i++ ] = turn;』ではなくて『check[ turn - 1 ] = 1;』にした方が良いでしょう。
 それからチェックは
 for ( flg = j = 0 ; j < 4 ; j++ ){
  if ( check[j] ){
   printf( "/*-----%sのターンは終了しています-----*/\n", dt[turn-1].name );
   flg = 1;
   break;
  }
 }
 とします。flg は変なところで初期化しないようにして下さい。見づらいよ。
・check[] 配列のロジックを直せばおかしな構造体データを操作しなくなります。
 これにより
>(1)dtを初期化しているのに、魔法を使おうとすると、mpが足りないと出る
>(2)お互いにダメージを与えているのに敵だけhpが減らない
 の2つが解決するはずです。
・以上。サブ関数に分けて作り直した方がデバッグするより早い気がします。

投稿日時 - 2007-08-29 08:56:37

お礼

まず疑問点について私がいうのも変なのですが、回答します。

(1)これは先に思いついた方のやり方を書いてました。
(2)このプログラムで、状態異常をまだ付けていないソースがあったのですが、その時実行した時には何もバクや文字化けなく動きました。
(3)一応全体攻撃として考えているので^^
(4)確かにchaAとchaB、chaCとchaDは殆ど同じ処理内容ですが、まだ追加処理を入れるつもりだったので、一人一人別々に作っていたのですm(_ _)m
(5)状態異常のときの処理は、まだ手付かずなので・・・。ごめんなさい。
ビットOR、これは使ったことがなかったので参考にさせていただきます。

実は、No16さん以降、ソースを追加したり修正しているうちに、文字化けやバグが無くなりました(汗)

投稿日時 - 2007-08-29 10:34:46

ANo.20

部分部分ばらばらに貼り付けずに、全部一緒に貼り付けちゃえばいいのに。。そんなに長くなさそうだし。

そのほうが、見てくれる方々も見やすいし、まぎれがなくていいでしょ^^

投稿日時 - 2007-08-29 00:29:35

ANo.19

★アドバイス
>基本的に文字化けしたり、値か変わったりするのは、ダメージを受けたあとになっているようです。
>本題ですが、nameのNULL文字以降調べようとしたのですが、調べ方が分からなくて;;
 ↑
 次のダンプ関数を作成して下さい。
 そしてダンプ表示したい場所で呼び出して下さい。

// 構造体の16進ダンプ表示
void printDump( status data[], size_t max, const char msg[] )
{
 FILE *fp;
 
 if ( (fp = fopen("dump16.txt","a")) != NULL ){
  fprintf( fp, "---- ↓[%s] ----\n", msg );
  
  for ( size_t n = 0 ; n < max ; n++ ){
   unsigned char *p = (unsigned char *)&data[n];
   unsigned char *end = (unsigned char *)&data[n + 1];
   
   fprintf( fp, "\n★status dt[%d]:%s\n", n, data[n].name );
   for ( int y = 0x00 ; p < end ; y += 0x10 ){
    fprintf( fp, "%08X: ", y );
    
    for ( int x = 0 ; (p < end) && (x < 0x10) ; x++ ){
     if ( x == 0x07 ){
      fprintf( fp, "%02X-", *p++ );
     }
     else{
      fprintf( fp, "%02X ", *p++ );
     }
    }
    fprintf( fp, "\n" );
   }
  }
  fprintf( fp, "---- ↑[%s] ----\n\n", msg );
  fclose( fp );
 }
}

// 使い方
printDump( dt, 5, "test(1)" );
printDump( dt, 5, "test(2)" ); ←ダンプ時のメッセージを指定可能

その他:
・上記の printDump() 関数をソースに組み込んで下さい。
 インデント部が全角空白文字なのでエディタでタブ文字に一括変換して下さい。
 それから dump16.txt というファイルに追加書き込みされていきます。
 メモ帳などで開いて確認して下さい。
・あと else 部分のソースもすべて貼り付けてくれますか。
 その他 human()、chaA()、chaB()、chaC()、chaD() 関数のソースもすべて貼り付けて欲しいです。
 ANo.19 の回答への補足、ANo.19 の回答へのお礼、
 ANo.18 の回答への補足、ANo.18 の回答へのお礼
 の順に else 部分、human()、chaA()、chaB()、chaC()、chaD() 関数を貼り付けて。
・以上。

投稿日時 - 2007-08-28 22:02:18

ANo.18

zwi

あっ、何度も申し訳ない!
単なる書き間違いでした。C言語で書いてますよ。
x0xfじゃなくて0x0fです。
すごい打ち間違いだな自分。

投稿日時 - 2007-08-28 18:04:59

ANo.17

zwi

あっ、さらに修正です。たびたび申し訳ない!。

for( int di=0 ; di<5 ; di++ ) {
unsigned char *pDump = (unsigned char*)&dt[di];
printf( "--- dump dt[%d] struct ---",di );
for( int dj=0 ; dj<sizeof(status) ; dj++,pDump++ ) {
if( (dj&x0xf)==0 ) printf("\n%04X",dj);
printf( "%02X ", *pDump );
}
printf("\n");
}
printf( "--- dump dt end ---\n" );

投稿日時 - 2007-08-28 17:42:48

補足

これはC++で書かれていますか?残念ながら私Cで書いてまして・・・。
取り合えず、ファイル名を「.cpp」に変えてやってみましたが、

エラー E2451 main.cpp 25: 未定義のシンボル x0xf(関数 main() )

っていうのが発生しました(汗)

投稿日時 - 2007-08-28 17:57:41

ANo.16

zwi

ダンプは、それでOKです。俗にダンプというとメモリ内容を16進数で表示させたものをいいます。
でも、ごめんなさい、ちょっと見づらかったですね(^^ゞ
今後のことを考えて修正しました。

for( int di=0 ; di<5 ; di++ ) {
unsigned char *pDump = (unsigned char*)&dt[di];
printf( "--- dump dt[%d] struct ---\n",di );
for( int dj=0 ; dj<sizeof(status) ; dj++,pDump++ ) {
printf( "%02X ", *pDump );
if( (dj&x0xf)==0xf ) printf("\n");
}
printf("\n");
}
printf( "--- dump dt end ---\n" );

これを正常な場合と異常な場合の2パターンお願いします。

投稿日時 - 2007-08-28 17:36:24

ANo.15

zwi

あっ書きもれました。
unsigned char *pDump = (unsigned char*)dt;
にしてください。

投稿日時 - 2007-08-28 17:07:45

補足

ごめんなさい。私そのダンプっていうのが良く分からないんで・・・

取り合えずこんなのをを表示すればいいのですか?
--- 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

投稿日時 - 2007-08-28 17:19:33

ANo.14

zwi

色々プログラムの修正が入ってますが、まだ再現する場合があるでしょうか?
まだ異常があるなら、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");
}

投稿日時 - 2007-08-28 17:06:02

ANo.13

> st[4]は、st_ab[4]の間違いでした。

これもそうですし、他にも転記する際に省略されたり、転記ミスがあったりしますね。
字下げは気にしなくてもよいので、実際に使用しているものをそのままコピー&ペーストしてください。
また、

> その場合、nameのナル文字より後がどうなるかも教えてください。

これも重要な手がかりになるので、補足してください。

投稿日時 - 2007-08-28 16:09:48

補足

戦闘場面のソースを載せます。

/*--------------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文字以降調べようとしたのですが、調べ方が分からなくて;;
基本的に文字化けしたり、値か変わったりするのは、ダメージを受けたあとになっているようです。

投稿日時 - 2007-08-28 16:37:20

ANo.12

★ワーニング出ませんでしたか?
>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] );
 とします。もう一度正しくポインタ値を表示してみて下さい。
・以上。→オプション設定でワーニングを一番高いレベルに設定しておきましょう。

投稿日時 - 2007-08-28 16:08:09

お礼

本当ミスばっかりで申し訳ありません(汗)

&をつけて表示させましたところ、ちゃんと表示されました。

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

投稿日時 - 2007-08-28 16:23:58

ANo.11

★追記。
・人物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回復っておかしいので。
・以上。

投稿日時 - 2007-08-28 15:58:50

補足

非常に親切なアドバイス、ありがとうございます。

御指摘された、hpの0以下の処理は、4000文字を考慮してわざと書きませんでした。誤解させてすいません(汗)。

アドバイスいただいたやり方の方が、分かり易そうですね!やってみます。
下記のコメントですが↓

あと human、chaA、chaB、chaC、chaD の関数の初期化を関数で初期化して~

この関数の初期化を関数で初期化してっていうのはどういう意味でしょうか?私の日本語の取り違いかも知れませんが、教えていただけると幸いですm(_ _)m

投稿日時 - 2007-08-28 16:08:05

ANo.10

★アドバイス
>作っているのは簡単な戦闘シュミレーションのプログラムです。
 ↑
 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] を渡すだけ。
 この方がプログラミングしやすいですよ。
・以上。いろいろと参考にして下さい。構造体がずれている気がしますので関数の整理を。

投稿日時 - 2007-08-28 15:43:46

ANo.9

zwi

ざっと、見る限りあやしい点はありません。
が、プログラムすべてが同じ構造体の定義を見ている場合に限りますが。
これ↓
typedef srtuct Status{
  char name[256];
  int hp,mp,st[4];  //st[4]は、状態異常を判定する配列
}status;
と同じような構造体は定義していませんよね?st[]とst_ab[]で2種類の名前があったので気になりました。
これが問題なければ、どこかでp++をしているとか、st[4~]のアクセスをしている所があるぐらいしか思いつきません。

投稿日時 - 2007-08-28 15:24:20

補足

す、すいません(汗)
st[4]は、st_ab[4]の間違いでした。

投稿日時 - 2007-08-28 15:27:16

ANo.8

専門家が見てくれると言ってくれているんだから、素直にべったり貼り付けるのがお得だと思いますよ^^

投稿日時 - 2007-08-27 17:46:08

ANo.7

この類の不具合は、コンパイルできて、かつ不具合が再現するソースを補足してもらわない限り原因が究明できる可能性は低いと思ってください。
また、どんな処理系を使っているのかも必須の情報です。

一度、char name[20]のところを、char name[256]ぐらいにして現象を確認してみてください。そして、その結果を教えてください。
その場合、nameのナル文字より後がどうなるかも教えてください。
(破壊がどこまで及んでいるかを知るためです)

投稿日時 - 2007-08-27 17:14:48

補足

補足要求の皆さん、遅れてすいませんでした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が減らない

以上、補足とさせていただきます。

投稿日時 - 2007-08-28 14:25:49

お礼

こんなことろに書いてすいません。補足にて足りないことがあったので。
開発環境は、
エディタ : 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

一応必要であれば。

投稿日時 - 2007-08-28 15:22:36

ANo.6

★私もhuman関数の中身が見たいです。
 あと
>void human(status *p);
 は
 void human(Status *p);
 だよね。
・ソースを貼り付けてみて下さい。

投稿日時 - 2007-08-27 17:11:13

ANo.5

zwi

Oh-Orangeさんではありませんが、見てみました。
mainの*pの初期化が謎ですが、それ以外は問題がなさそうです。
どちらかと言うとhuman関数の方が危険そうですね。human関数の中身が見たいです。

投稿日時 - 2007-08-27 17:01:10

ANo.4

zwi

構造体の問題ではなさそうです。
プログラムの何処かでメモリ破壊をしている箇所があり、たまたま構造体の内容が壊れているだけだと思います。
よくある失敗として、
1.ポインタの扱いが間違っていて、本来の場所以外を指している。
2.配列の添え字の数値が、配列の宣言時の値を超えていて配列外を壊している。
3.malloc、freeで管理しているメモリ領域が解放されているにもかかわらず。解放されていること確認しないで使ってしまっている。そのため後からmallocで同じ場所に構造体を割り当ててしまってメモリが破壊される。
などが考えられます。
たまに、このような事が起こるとすれば、いつも同じ様なプログラムのミスをしている可能性が高いです。放置しておかずに、その悪いプログラミングのクセを直したほうがよいでしょう。そのためには破壊する箇所を探しだすことです、なぜこうなるのかを追求しないといつもでも謎の破壊は直りませんよ。

投稿日時 - 2007-08-27 16:20:47

お礼

回答、そして忠告ありがとうございます。

失敗例を元に、もう一度プログラムを見直してみます。

投稿日時 - 2007-08-27 16:31:09

ANo.3

単にプログラムミスの可能性が大ですよね^^

ふーん、そうなのか。。。仕様で、構造体のメンバの個数に最低保証数みたいなものを規定してあるのというのは、初めて知りました。

投稿日時 - 2007-08-27 15:42:15

ANo.2

★アドバイス
>構造体のメンバが例えば10個とかあったりしたら、
>何かプログラムに異常が起きたりするものでしょうか?
 ↑
 どんな異常が起きますか?
 メモリが足りていれば問題はないと思います。
>本来入っているはずの数値(文字列)が、微妙にでたらめなものに変わってしまいます。
>(実際に起きたのが、int aの値は10なのに、46543とか。
>char name[20] = "ジタン" が、"ジタnp"とか)
 ↑
 構造体データをちゃんと初期化していますか?
>たまに、こういったことがプログラムを組んでいると起こるので、
>何が起こっているのか分かる方、御教授お願いします。
 ↑
 構造体の扱い方が正しくないのでは?
 これぐらいしか分かりません。
・その他、構造体をファイルに保存してそこから読み込むときに位置がずれている事はありませんか?
 また構造体の境界(パディング)を変更してコンパイルしたのにファイル内容は変更前のままとか?
 構造体とか、その操作する処理を見ないと分かりませんね。
 いろいろと補足して下さい。
・以上。

投稿日時 - 2007-08-27 15:12:35

補足

回答ありがとうございます。
長くなりそうなので、簡単に補足させていただきます。分割コンパイルです。

/*-----myhead.h-----*/
typedef struct status{
char name[20];
int a,b,c,st[4];
}Status;

void human(status *p);

/*-----main.c-----*/
Status dt[5] = {{"人物A",10,20,30,{0,0,0,0},~{"人物E",10,20,30,{0,0,0,0}}};
int i,j;

for(i=0;i<5;i++){
printf("%-10s %d %d",(p+i)->name,(p+i)->a,(p+i)->b);
for(j=0;j<4;j++)
printf("%d ",(p+i)->st[j]);
printf("\n");
}

//初期化後に、別関数に渡す
human(dt);

for(i=0;i<5;i++){
printf("%-10s %d %d",(p+i)->name,(p+i)->a,(p+i)->b);
for(j=0;j<4;j++)
printf("%d ",(p+i)->st[j]);
printf("\n");
}
//この表示するときに、値がでたらめになっていることがある

/*-----human.c-----*/
void human(status *p){
/*この関数でメンバの値(name以外)を変化させる処理を行っている*/
}

投稿日時 - 2007-08-27 16:25:15

ANo.1

構造体のメンバの数ですが、最も制約の厳しいC90でも、少なくとも127個までは保証されます。
というわけで、今回の不具合は構造体のメンバ数とは関係ないと思います。ただし、処理系のバグであれば、この限りではありません。

投稿日時 - 2007-08-27 15:04:25

あなたにオススメの質問