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

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

新規登録して質問してみよう
ただいま回答率
85.50%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

2回答

325閲覧

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

houki

総合スコア22

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

1クリップ

投稿2018/10/28 07:31

前提・実現したいこと

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

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

特にありません。

該当のソースコード

c

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <ctype.h> 5#include <time.h> 6 7struct result 8{ 9 char *num; 10 int eat; 11 int bite; 12 int times; 13}; 14 15void gr_random(void); 16void check(int, int, int); 17void item(void); 18int HIGHLOW(void); 19int TARGET(void); 20int SLASH(void); 21void result(struct result *p, int ar_r); 22 23int a, b, c; //関数の関係上グローバル変数とした 24struct result res[7]; 25 26int main(void) 27{ 28 printf("!チャレンジは7回!\n"); 29 printf("数値入力してください。アイテム[a]/入力履歴[r]/強制終了[f]\n\n"); 30 printf("\t\t\t\t 1回目の入力>>"); 31 32 gr_random();//相手数字のランダム生成 33 check(a, b, c); 34 35 return 0; 36} 37 38void gr_random(void) 39{ 40 srand((unsigned)time(NULL)); 41 42 a = rand() % 10 + 0; 43 b = rand() % 10 + 0; 44 while (b == a) 45 { 46 b = rand() % 10 + 0; 47 } 48 c = rand() % 10 + 0; 49 while (c == a || c == b) 50 { 51 c = rand() % 10 + 0; 52 } 53 54} 55 56void check(int x, int y, int z) 57{ 58 int i = 0, j = 0, r = 1, t = 1, k = 0, l = 0; 59 60 char str[3]; 61 char e, f, g; 62 int exchange_num; 63 64 for (r = 1; r <= 7; r++) 65 { 66 int count = 0; 67 scanf("%s", str); 68 //アルファベットの処理 69 70 //入力の桁数check 71 if (strlen(str) == 3) 72 { //重複check ただし正しく3桁入力された時のみ重複のcheckをする 73 if (isdigit(str[0]) == 0 || isdigit(str[1]) == 0 || isdigit(str[2]) == 0) 74 { //数値以外が入力された時 75 printf("数値を入力してください! もう一度%d回目の入力>>", r); 76 r--; 77 continue; 78 } 79 if (str[0] == str[1] || str[0] == str[2] || str[1] == str[2]) 80 { 81 printf("⚠︎重複⚠︎\t\t\t もう一度%d回目の入力>>", r); 82 r--; 83 continue; 84 } 85 } 86 else 87 { 88 89 switch (str[0]) 90 { 91 //1桁でも特殊なパターン 92 case 'a': 93 item(); //アイテム使用 94 count++; 95 break; 96 case 'f': //ゲーム終了 97 r = 10; 98 break; 99 case 'r': 100 result(res, r); //今までの履歴を表示 101 count++; 102 break; 103 } 104 if (r == 10) 105 break; //'f'が入力された時 106 107 if (count == 0) 108 printf("⚠︎桁数⚠︎\t\t\t もう一度%d回目の入力>>", r); 109 else 110 printf("\t\t\t もう一度%d回目の入力>>", r); 111 112 r--; 113 continue; 114 } 115 116 //-------------------------------------------------------ここまでが入力値の桁や重複についての処理。以下は数値(=文字)比較の処理。 117 118 e = '0' + x; 119 f = '0' + y; //数値a,b,cを文字型の数字に変換 120 g = '0' + z; //以下は文字としての数字比較をの処理 ''は要らない 121 122 //百の位 123 if (str[0] == e) 124 i++; 125 else if (str[0] != e && (str[0] == f || str[0] == g)) 126 j++; 127 //十の位 128 if (str[1] == f) 129 i++; 130 else if (str[1] != f && (str[1] == e || str[1] == g)) 131 j++; 132 //一の位 133 if (str[2] == g) 134 i++; 135 else if (str[2] != g && (str[2] == e || str[2] == f)) 136 j++; 137 138 res[r - 1].num = str; 139 res[r - 1].eat = i; 140 res[r - 1].bite = j; 141 res[r - 1].times = r; 142 143 printf("%s\t%dE %dB\t(%-d回目)", res[r - 1].num, res[r - 1].eat, res[r - 1].bite, res[r - 1].times); 144 145 //3Eの時 146 if (i == 3) 147 break; 148 149 for (; t == r && t < 7; t++) 150 { 151 printf("\t\t%2d回目の入力>>", t + 1); //t+1に注意 152 } 153 i = 0; 154 j = 0; 155 } 156 157 if (r <= 7) 158 printf("\n正解です!!\n答えは%d%d%d!(・ω・)bグッ\n", x, y, z); 159 else if (r == 8) 160 { 161 printf("\n残念。。。\n答えは%d%d%dでした。(・ω・)bグッ\n", x, y, z); 162 } 163 else 164 { 165 printf("チャレンジ終了を選択しました。 答えは%d%d%dでした。(・ω・)bグッ\n", x, y, z); 166 } 167} 168 169void item(void) 170{ 171 int h = 1, t = 1, s = 1; 172 static int l, m, n; //staticをつけて記憶寿命を伸ばした→関数を呼ぶたびに初期化されないようにした。本当のグローバル関数とは異なる。 173 char ch; 174 175 printf("\nHIGH&LOW(%d)/TARGET(%d)/SLASH(%d)\n", h - l, t - m, s - n); 176 177 if ((h - l) + (t - m) + (s - n) != 0) 178 { 179 printf("どのアイテムを使いますか?\n"); 180 printf("HIGH&LOW→[h] /TARGET→[t] /SLASH→[s] /戻る→[h][t][s]以外を入力してください。> "); 181 182 rewind(stdin); //前回の入力に改行コードが自動的に入っているので、これで抜け出す 183 scanf("%c", &ch); //putchar(ch);ch=getchar();でも可能 184 185 if (ch == 'h' && l != 1) 186 l = HIGHLOW(); 187 else if (ch == 't' && m != 1) 188 m = TARGET(); 189 else if (ch == 's' && n != 1) 190 n = SLASH(); 191 else 192 printf("戻りました。\n"); 193 } 194 else 195 printf("アイテムは使用できません。 戻ります。\n"); 196} 197 198int HIGHLOW(void) 199{ 200 int h = 0, l = 0; 201 202 if (a >= 5) 203 h++; 204 else 205 l++; 206 207 if (b >= 5) 208 h++; 209 else 210 l++; 211 212 if (c >= 5) 213 h++; 214 else 215 l++; 216 217 printf("\n%dHIGH %dLOW\n\n", h, l); 218 219 return 1; //アイテムは1つにつき1回までとした 220} 221 222int TARGET(void) 223{ 224 int t; 225 226 printf("\nどの桁を調べますか?\n"); 227 printf("調べたい数値を入力してください。>"); 228 scanf("%d", &t); 229 230 if (t == a) 231 printf("%d は100の桁にあります。", t); 232 else if (t == b) 233 printf("%d は10の桁にあります。", t); 234 else if (t == c) 235 printf("%d は1の桁にあります。", t); 236 else 237 printf("%d は含まれていません。", t); 238 239 return 1; //アイテムは1つにつき1回までとした 240} 241 242int SLASH(void) 243{ 244 int sla[3]; 245 int i, j; 246 int tmp; 247 248 sla[0] = a; 249 sla[1] = b; 250 sla[2] = c; 251 252 for (i = 0; i < 3; i++) 253 { 254 for (j = i; j < 3; j++) 255 { 256 if (sla[i] < sla[j]) 257 { 258 tmp = sla[i]; 259 sla[i] = sla[j]; 260 sla[j] = tmp; 261 } 262 } 263 } 264 printf("\nSLASHナンバーは「%d」です。", sla[0] - sla[2]); 265 266 return 1; 267} 268 269void result(struct result *p, int ar_r) 270{ 271 int counta; 272 273 printf("\n<結果履歴>\n"); 274 275 for (counta = 0; counta < ar_r - 1; counta++) 276 printf("%s\t%dE %dB\t(%-d回目)\n", (p + counta)->num, (p + counta)->eat, (p + counta)->bite, (p + counta)->times); 277} 278

試したこと

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

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

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

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

c

1char str[3];

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

つまり

c

1res[r - 1].num = str;

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

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

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

さあ、もう

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

の答えはわかりますね?


追記

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

c

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

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

c

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

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

c

1a = rand() % 10 + 0;

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

c

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

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

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

c

1rewind(stdin);

やめましょう。

c

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

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

c

1scanf("%d", &t);

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

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

投稿2018/10/28 10:30

編集2018/10/28 12:44
yumetodo

総合スコア5850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

houki

2018/10/28 10:59

先頭のアドレスですね。 自分は何か勘違いしているようなのですが、 char *moji; char str[10]="Hello"; moji="Hello"; moji=str; この2つは別物という認識であっていますか? http://www.isl.ne.jp/pcsp/beginC/C_Language_14.html の例題4 参照渡し を参考にしてポインタについてプログラムしました。 初期化と途中での代入は異なるのかな。。。?
yumetodo

2018/10/28 12:31

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

0

c

1struct result 2{ 3 char *num; 4 int eat; 5 int bite; 6 int times; 7};

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

c

1struct result 2{ 3 char num[4]; 4 int eat; 5 int bite; 6 int times; 7};

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

また、この変更で

c

1 res[r - 1].num = str;

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

c

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

等で対処してください


その他気になるところ

c

1 char str[3];

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

投稿2018/10/28 08:11

asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

houki

2018/10/28 10:16

思った通りの表示ができました!ありがとうございます。 今回のrが表示されていた原因はなんだったのでしょうか? また struct result { char *num; int eat; int bite; int times; }; とした場合 res[r - 1].num = str; で格納していたのはアドレスではなく、文字列ですか?
asm

2018/10/28 11:54

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問