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

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

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

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

ログイン

ログインは、ユーザーがコンピューターシステムにアクセスするプロセスの事を呼びます。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

Q&A

解決済

1回答

1145閲覧

ログイン画面のユーザー数共有機能について

teruto

総合スコア1

C

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

ログイン

ログインは、ユーザーがコンピューターシステムにアクセスするプロセスの事を呼びます。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

0グッド

0クリップ

投稿2021/07/08 02:47

前提・実現したいこと

ここに質問の内容を詳しく書いてください。
ログイン画面に現在のユーザー数が表示されるという機能をつくっているのですが、ユーザーを追加する関数でユーザー数を増やした時にそのログイン画面に表示されるユーザー数が増えた状態にするにはどうすればいいですか?
scanf関数にスペースを入力した時スペースがそのまま表示されるようにするにはどうすればいいですか?
例 ログイン画面 現在のユーザー数;3
新しくユーザー数が増加
ログイン画面 現在のユーザー数;4

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

エラーメッセージ

該当のソースコード

C

ソースコード

int main(void)
{
Login();

return 0;

}
void Login(void)
{
int people;

char inputId[10];//ユーザーIDを入力するための変数 char inputPass[10];//パスワードを入力するための変数 printf("現在登録されているユーザーは%d名です\n\n", people); do { rewind(stdin); printf("ログイン画面\nユーザーIDとパスワードを入力してください。(終了はquit)\nユーザーID:"); scanf("%s", inputId);//ユーザーIDを入力 if (!strcmp(inputId, "quit"))//見やすくするために!(否定)を入れている { // inputIdの中身とquitが一致するかどうか判断する break;//文字列"quit"が入力されたら処理終了 } printf("パスワード:"); rewind(stdin); scanf("%s", inputPass);//パスワードを入力 /*入力したIDとパスワードが設定したIDとパスワードと一致しているかどうかの確認*/ for (int kurikaesi = 0; kurikaesi < 5; kurikaesi++) { if (!strcmp(inputId, userID[kurikaesi]) && !strcmp(inputPass, userPass[kurikaesi])) { MenuDisplay(people); } } printf("ユーザーIDおよびパスワードが異なっています。\n"); } while (1);//条件式に1を入れているのは無限ループにするため

}

void MenuDisplay(int player)
{
int inputNo;//使用したい機能の番号を入れるための変数

do { printf("\n\n機能選択画面\n1:大文字小文字逆転機能\n2:BMI測定機能\n3:ユーザー登録機能\n0:ログイン画面に戻ります\n"); printf("使用したい機能の番号(1~3)を入力してください:"); rewind(stdin); scanf("%d", &inputNo);//機能を選択するための数字を入力するためのもの switch (inputNo)//選んだ機能に分岐するためのもの { case 0: printf("\n\nログイン画面に戻りました\n\n\n"); Login(); break; case 1: LargSmallReverse(); break; case 2: BMICheck(); break; case 3: UserAdd(player); break; } printf("番号入力ミスです。"); } while (1);

}
int UserAdd(int human)
{
char repeat = 0;//ユーザー登録を続けるかどうかを判別するための文字を入れるための変数

PlayerCatalog(human); if (human <= 4)//ユーザーIDとパスワードを新しく追加できる { printf("\n新規追加のユーザーIDとパスワードを入力してください。\n"); do { printf("ユーザーID:"); scanf("%s", userID[human]);//新しいユーザーIDを入力するためのもの while (1) { if (isalpha(userID[human][0]))//新規追加のユーザーIDの最初の文字がアルファベットかどうか { break; } else { printf("ユーザーIDの最初の文字はアルファベットでお願いします\n"); printf("ユーザーID:"); scanf("%s", userID[human]);//新しいユーザーIDを入力するためのもの } } for (int repeat = 0; repeat < human; repeat++) { if (!(strcmp(userID[human], userID[repeat])))//新しく追加するIDが登録されているIDと被っていないかの確認 { printf("既にそのユーザーIDは登録されています\n新規追加のユーザーIDとパスワードを入力してください\n"); printf("ユーザーID:"); scanf("%s", userID[human]);//新しいユーザーIDを入力するためのもの repeat = -1;//また繰り返せるように初期化してある。-1で初期化しているのは+された時に0から始めれるように } } printf("パスワード:"); scanf("%s", userPass[human]);//新しいパスワードを入力するためのもの human = PlayerNumber(human);//ユーザー数の追加 PlayerCatalog(human); printf("\n"); if (human == 4)//残り1枠のユーザー登録をするかどうかの確認 { printf("更にユーザー登録をしますか?(y/n):"); rewind(stdin); scanf("%c", &repeat); while (repeat != 'n' && repeat != 'y') { printf("入力する文字は y or n です\n更にユーザー登録をしますか?(y/n):"); rewind(stdin); scanf("%c", &repeat); } if (repeat == 'n') { printf("ユーザー追加機能を終了します\n機能選択画面に戻りました"); return human; } } } while (human <= 4); } if (human == 5) { printf("ユーザー追加限界数まで登録しました\nこれ以上の追加は不可能ですので、メニューに戻ります\n機能選択画面にもどりました\n"); return human; }

}

試したこと

ネットでいろいろ調べてみたのですがわかりませんでした。

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

PlayerCatalog関数はユーザーIDとパスワードを表形式で表示する機能です。

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

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

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

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

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

hoshi-takanori

2021/07/08 05:23

登録したユーザーはどこに保存するのでしょうか?
dodox86

2021/07/08 05:46

> ネットでいろいろ調べてみたのですがわかりませんでした。 この様なローカルな仕様はググッて調べるものでは無く、自分で考えて作るものなのですけれども。
jimbe

2021/07/09 18:10

再帰してます。
guest

回答1

0

ベストアンサー

scanf関数にスペースを入力した時 スペースがそのまま表示されるようにするにはどうすればいいですか?

要するに、スペースを含んだ文字列を入力したいという事でしょう。scanf() の基本動作は入力文字列をスペース文字で区切るので、普通の使い方ではスペースの入った文字列をひとまとまりで入力できません。
どうすればいいか。少しツッコんだ使い方をしなければなりません。

空白、タブの読み飛ばし(scanf, wikipedia) に次の2例が挙がっています。

C

1 char a[20]; // この配列に読み込むとして 2 scanf("%[^\n]", a); // 改行文字以外の文字列を全て a に代入する 3 scanf("%[^\n]%*c", a); // 入力時の改行コードの取り扱いも考慮した場合

ただユーザ名パスワードをそれぞれ一行で読み込むのだから、scanf()ではなく、
fgets(a, sizeof(a), stdin); で入力するのがお勧めです。改行文字 \n も読み込まれることなど注意点もありますが、仕組みは scanf() よりシンプルで扱いやすいと思いますので。


ユーザーを追加する関数でユーザー数を増やした時に
そのログイン画面に表示されるユーザー数が増えた状態にするにはどうすれば

この問題には大きく2つの原因があると思います。
ひとつは変数 people の取り方・使い方、もうひとつは** Login() 関数の呼び方**です。
どちらも、C言語の理解が足りないまま、プログラムの動作あるいは設計を考えないまま、よく分からずに書き進めてしまった、という状況を想像します。

まず変数 people の取り方。

C

1void Login(void) 2{ 3 int people; // ← 未初期化ローカル変数、即ち不定値 4 // 途中料略 5 printf("現在登録されているユーザーは%d名です\n\n", people);

people 変数はどんな変数か。

  • people は初期値の無いローカル変数
  • 値は不定値(いくつになるか決まらない)
  • 表示される値には根拠も意味も無い(不定値だから)
  • Login() 関数が呼ばれる都度、新たに people 変数が作られる(後述)
  • この関数以外で参照できない(というコードになっている)

ユーザー数共有機能について

共有するには、データをどこに・どう保存するか、を考えないといけません。
**静的変数にする(グローバル変数かstatic変数)**が安直な対策です。でも、これはプログラムの動作環境、データ構造、使い勝手などを勘案しながら全体を見て決める(設計する)ことなので、私らが結論を出せるものではありません。まず、そのやり方じゃダメということを理解してください。


もうひとつの原因、Login() 関数の呼び方。
処理の流れ・プログラムの作り方はこんな感じになっています。

  1. まずはログインしないと始まらないだろう、と main() 関数が Login() を呼出す
  2. Login() の中で入力画面を作り、入力を受け付ける
  3. 入力文字列が一致しなかったら MenuDisplay(people) を呼出し、メニュー画面を表示する
  4. MenuDisplay()の関数の中で「ログイン画面に戻ります」を選択すると、再び Login() を呼出す

Login() 関数の中から(直接ではないが)さらに Login() を呼ぶ・・・つまり再帰呼出しをした格好ですが、分かって書いているとは思えません。メニューの表示は「戻ります」ですが、実は関数呼出しから戻ってはいないのです。

ユーザー数を増やした後、ログイン画面に進んだら、ユーザー数が増えていない、という現象だったのでしょう(people変数の初期値は不定なので、たまたま同じ人数が表示されたと思われる)。
しかし、2つ目の Login() は再帰的に新たに呼ばれた関数として動作します。
Login() 関数が呼ばれる都度、新たに people 変数が作られるので、

  • 一度目に呼ばれた Login() が確保し操作する people 変数
  • 二度目に呼ばれた Login() が確保し操作する people 変数

この2つの people 変数は同じ変数ではありません
名前は同じでも、番地が違うメモリに割当てられた、違う値を持ちうる、別々の変数だという事。一方を増やしたからと言って片方が連動して増えたりはしません。
前と同じ画面が表示されても「戻った」のではない事を理解できるでしょうか。

さて、わからないうちに再帰呼出ししたプログラムですが、初心者が「こうなった場合はこうしなければ」的な思いつきレベルで書くとこんな風になることがあります。悪く言えば、思いつきで・行き当たりばったりで書いているように見えます。
このプログラムの懸念事項は次のようなもの。

  • ユーザが入力を誤り続ければ、関数呼出しがどんどん深くなり(ネストが深くなるとも言う)、スタックオーバーフローを起こしかねない
  • 呼出された関数はいずれ呼出し元へリターンする(しなければならない)が、安全に・期待通りに動くだろうか
  • コードがスパゲッティ化し破綻を招く

正しく入力されると限らないユーザの入力を処理する際に、思いつきで処理の流れを作ろうとするのは厄介なものです。手に負えなくなることもあるし、できたとしてもその後の手直しが面倒になります。

こんな場合どうするか。私なら、まず状態遷移設計を検討します。
とりいそぎ「より良いシステム開発のために、状態遷移設計のことを知ってほしい」というページをご紹介します。ここには「ゲーム開発や画面遷移といった開発にも適用され」るとあります。
状態遷移設計なら関数呼出しが深くなることはなく、ユーザが入力を間違え続けてもビクともしないプログラムを作ることができます。もっとも、状態遷移マシンを作るには練習が要ると思いますし、検討できる他の手段もあるわけですが。

最後に。無限ループは do { ~ } while (1); ではなく
while (1) { ~ } もしくは for (;;) { ~ } にしましょう。ループの先頭で無限ループだと分かるほうが良いに決まってるから。

・・・という事で、まだまだ調べる事も学ぶ事もたくさんあります。
Enjoy!

投稿2021/07/10 06:11

rubato6809

総合スコア1382

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

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

teruto

2021/07/13 13:02

回答ありがとうございました。 回答を参考に頑張ってみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問