質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.64%

構造体配列とポインタについて(初心者です)

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 762

houki

score 22

 前提・実現したいこと

初心者です。
構造体配列とポインタ、を上手に使いたい。
自分なりのNumer0nを作成しました。
"r"と入力して、result関数で結果表示させると、今まで打ってきた数字が表示されるはずなのですが、全てrと表示され、解決策がわかりません。
解決策を教えてください。

 発生している問題・エラーメッセージ

特にありません。

 該当のソースコード

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

struct result
{
  char *num;
  int eat;
  int bite;
  int times;
};

void gr_random(void);
void check(int, int, int);
void item(void);
int HIGHLOW(void);
int TARGET(void);
int SLASH(void);
void result(struct result *p, int ar_r);

int a, b, c; //関数の関係上グローバル変数とした
struct result res[7];

int main(void)
{
  printf("!チャレンジは7回!\n");
  printf("数値入力してください。アイテム[a]/入力履歴[r]/強制終了[f]\n\n");
  printf("\t\t\t\t 1回目の入力>>");

  gr_random();//相手数字のランダム生成
  check(a, b, c);

  return 0;
}

void gr_random(void)
{
  srand((unsigned)time(NULL));

  a = rand() % 10 + 0;
  b = rand() % 10 + 0;
  while (b == a)
  {
    b = rand() % 10 + 0;
  }
  c = rand() % 10 + 0;
  while (c == a || c == b)
  {
    c = rand() % 10 + 0;
  }

}

void check(int x, int y, int z)
{
  int i = 0, j = 0, r = 1, t = 1, k = 0, l = 0;

  char str[3];
  char e, f, g;
  int exchange_num;

  for (r = 1; r <= 7; r++)
  {
    int count = 0;
    scanf("%s", str);
    //アルファベットの処理

    //入力の桁数check
    if (strlen(str) == 3)
    { //重複check ただし正しく3桁入力された時のみ重複のcheckをする
      if (isdigit(str[0]) == 0 || isdigit(str[1]) == 0 || isdigit(str[2]) == 0)
      { //数値以外が入力された時
        printf("数値を入力してください! もう一度%d回目の入力>>", r);
        r--;
        continue;
      }
      if (str[0] == str[1] || str[0] == str[2] || str[1] == str[2])
      {
        printf("⚠︎重複⚠︎\t\t\t もう一度%d回目の入力>>", r);
        r--;
        continue;
      }
    }
    else
    {

      switch (str[0])
      {
      //1桁でも特殊なパターン
      case 'a':
        item(); //アイテム使用
        count++;
        break;
      case 'f': //ゲーム終了
        r = 10;
        break;
      case 'r':
        result(res, r); //今までの履歴を表示
        count++;
        break;
      }
      if (r == 10)
        break; //'f'が入力された時

      if (count == 0)
        printf("⚠︎桁数⚠︎\t\t\t もう一度%d回目の入力>>", r);
      else
        printf("\t\t\t もう一度%d回目の入力>>", r);

      r--;
      continue;
    }

    //-------------------------------------------------------ここまでが入力値の桁や重複についての処理。以下は数値(=文字)比較の処理。

    e = '0' + x;
    f = '0' + y; //数値a,b,cを文字型の数字に変換
    g = '0' + z; //以下は文字としての数字比較をの処理 ''は要らない

    //百の位
    if (str[0] == e)
      i++;
    else if (str[0] != e && (str[0] == f || str[0] == g))
      j++;
    //十の位
    if (str[1] == f)
      i++;
    else if (str[1] != f && (str[1] == e || str[1] == g))
      j++;
    //一の位
    if (str[2] == g)
      i++;
    else if (str[2] != g && (str[2] == e || str[2] == f))
      j++;

    res[r - 1].num = str;
    res[r - 1].eat = i;
    res[r - 1].bite = j;
    res[r - 1].times = r;

    printf("%s\t%dE %dB\t(%-d回目)", res[r - 1].num, res[r - 1].eat, res[r - 1].bite, res[r - 1].times);

    //3Eの時
    if (i == 3)
      break;

    for (; t == r && t < 7; t++)
    {
      printf("\t\t%2d回目の入力>>", t + 1); //t+1に注意
    }
    i = 0;
    j = 0;
  }

  if (r <= 7)
    printf("\n正解です!!\n答えは%d%d%d!(・ω・)bグッ\n", x, y, z);
  else if (r == 8)
  {
    printf("\n残念。。。\n答えは%d%d%dでした。(・ω・)bグッ\n", x, y, z);
  }
  else
  {
    printf("チャレンジ終了を選択しました。 答えは%d%d%dでした。(・ω・)bグッ\n", x, y, z);
  }
}

void item(void)
{
  int h = 1, t = 1, s = 1;
  static int l, m, n; //staticをつけて記憶寿命を伸ばした→関数を呼ぶたびに初期化されないようにした。本当のグローバル関数とは異なる。
  char ch;

  printf("\nHIGH&LOW(%d)/TARGET(%d)/SLASH(%d)\n", h - l, t - m, s - n);

  if ((h - l) + (t - m) + (s - n) != 0)
  {
    printf("どのアイテムを使いますか?\n");
    printf("HIGH&LOW→[h] /TARGET→[t] /SLASH→[s] /戻る→[h][t][s]以外を入力してください。> ");

    rewind(stdin);    //前回の入力に改行コードが自動的に入っているので、これで抜け出す
    scanf("%c", &ch); //putchar(ch);ch=getchar();でも可能

    if (ch == 'h' && l != 1)
      l = HIGHLOW();
    else if (ch == 't' && m != 1)
      m = TARGET();
    else if (ch == 's' && n != 1)
      n = SLASH();
    else
      printf("戻りました。\n");
  }
  else
    printf("アイテムは使用できません。 戻ります。\n");
}

int HIGHLOW(void)
{
  int h = 0, l = 0;

  if (a >= 5)
    h++;
  else
    l++;

  if (b >= 5)
    h++;
  else
    l++;

  if (c >= 5)
    h++;
  else
    l++;

  printf("\n%dHIGH %dLOW\n\n", h, l);

  return 1; //アイテムは1つにつき1回までとした
}

int TARGET(void)
{
  int t;

  printf("\nどの桁を調べますか?\n");
  printf("調べたい数値を入力してください。>");
  scanf("%d", &t);

  if (t == a)
    printf("%d は100の桁にあります。", t);
  else if (t == b)
    printf("%d は10の桁にあります。", t);
  else if (t == c)
    printf("%d は1の桁にあります。", t);
  else
    printf("%d は含まれていません。", t);

  return 1; //アイテムは1つにつき1回までとした
}

int SLASH(void)
{
  int sla[3];
  int i, j;
  int tmp;

  sla[0] = a;
  sla[1] = b;
  sla[2] = c;

  for (i = 0; i < 3; i++)
  {
    for (j = i; j < 3; j++)
    {
      if (sla[i] < sla[j])
      {
        tmp = sla[i];
        sla[i] = sla[j];
        sla[j] = tmp;
      }
    }
  }
  printf("\nSLASHナンバーは「%d」です。", sla[0] - sla[2]);

  return 1;
}

void result(struct result *p, int ar_r)
{
  int counta;

  printf("\n<結果履歴>\n");

  for (counta = 0; counta < ar_r - 1; counta++)
    printf("%s\t%dE %dB\t(%-d回目)\n", (p + counta)->num, (p + counta)->eat, (p + counta)->bite, (p + counta)->times);
}

 試したこと

普通に入力した際に表示される数字はポインタは使っていませんが、しっかり表示されます。
しかし、"r"と入力して結果表示をするときは構造体の配列をアドレス渡ししてポインタを利用して、結果表示させたいのですが、うまく表示できません。

 補足情報(FW/ツールのバージョンなど)

入力する数字などは、全てchar型です。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

まず文字列の定義を再確認しましょう。Cにおいて文字列といえば、NULL文字終端するbyte列を指します。すると文字列を代入という概念そのものが成立しないことがわかります。

ここでstrの宣言を確認すると

char str[3];

となっているようです。さて、配列は3つくらいの例外を除き常にポインタに読み替えられます(Array to Pointer Conversion)。

つまり

res[r - 1].num = str;

は、配列の先頭要素へのポインタを代入する作業となっていたのでした。asmさんの回答にある

numが全てローカル変数strを指しています。

の意味がお分かりいただけたでしょうか?

さあ、もう

格納していたのはアドレスではなく、文字列ですか?

の答えはわかりますね?


追記

配列を文字列リテラルで初期化できるというのは特例と考えるべきです。

char str1[] = "arikitari na sekai";//OK
char str2[] = { "arikitari na sekai" };//OK
char str3[19] = { 0 };
//str3 = "arikitari na sekai";//NG

さて、お示しのコードを解釈していきましょう(constを付け加えています、文字列リテラルが格納される領域は書き換えてはいけないので)

const char *moji;
char str[10]="Hello";//配列を文字列リテラルで初期化

moji="Hello";//Cだとmojiを文字列リテラルが格納される領域へのポインタで初期化(C++では代入)
moji=str;//配列変数strの先頭要素へのポインタをmojiに代入

ほんだいからずれますが、ちょっと突っ込ませてください。

a = rand() % 10 + 0;

乱数の範囲調整に剰余を用いてはいけません。

(isdigit(str[0]) == 0 || isdigit(str[1]) == 0 || isdigit(str[2]) == 0)

3桁だからこれでもなんとかなってますが、strtol系関数の利用を検討してください。

C言語で安全に標準入力から数値を取得 - Qiita

rewind(stdin);

やめましょう。

scanf("%c", &ch); //putchar(ch);ch=getchar();でも可能

scanfは冗長じゃないかなぁ・・・。putcharはお話しにならんですが、getcharなんかを使えばいいかと

scanf("%d", &t);

scanfで数値変換するべきではありません。

C言語で安全に標準入力から数値を取得 - Qiita

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/28 19:59

    先頭のアドレスですね。

    自分は何か勘違いしているようなのですが、
    char *moji;
    char str[10]="Hello";

    moji="Hello";
    moji=str;
    この2つは別物という認識であっていますか?

    http://www.isl.ne.jp/pcsp/beginC/C_Language_14.html
    の例題4 参照渡し を参考にしてポインタについてプログラムしました。
    初期化と途中での代入は異なるのかな。。。?

    キャンセル

  • 2018/10/28 21:31

    ちなみにポインタをアドレスと呼ぶのは個人的には好きではないので意地でもポインタと表記しています。あしからず。

    キャンセル

0

struct result
{
  char *num;
  int eat;
  int bite;
  int times;
};

numが全てローカル変数strを指しています。
3文字の文字列を格納するためには

struct result
{
  char num[4];
  int eat;
  int bite;
  int times;
};


と格納するための場所を用意する必要があります。

また、この変更で

    res[r - 1].num = str;


等でエラーが出るはずなので

    strcpy(res[r - 1].num, str);


等で対処してください


その他気になるところ

  char str[3];

上述しましたが、3文字の文字列を格納するためには最低4文字分の場所が必要です。
3文字以上を入力するとバッファオーバーフローが発生するのは学習用途でさえも危険なように思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/28 19:16

    思った通りの表示ができました!ありがとうございます。

    今回のrが表示されていた原因はなんだったのでしょうか?

    また
    struct result
    {
    char *num;
    int eat;
    int bite;
    int times;
    };
    とした場合

    res[r - 1].num = str;
    で格納していたのはアドレスではなく、文字列ですか?

    キャンセル

  • 2018/10/28 20:54

    char*型はポインタなのでアドレスしか入りません。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る