アドレス帳を作っています。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ソラ
記事: 4
登録日時: 2年前

アドレス帳を作っています。

#1

投稿記事 by ソラ » 2年前

学校の課題でアドレス帳を作っています。
期限は月曜日に提出です。
詳細の仕様はwordファイルを参照してください。
今わかる範囲でプログラムを書きました。
詰まっているのはアドレス帳要件(wordファイル)の1と2と4です。
メニューに戻る処理にはcontinueを使うと思って使ってます。 ←自分なりに調べた結果
今知りたいのは、エラーが起きた時のメニューに戻る処理とアドレス表示した後にもメニューを表示させたいです。
OS:windows
コンパイラ:visual stdio 2019
ライブラリ:string
c言語の理解度は「初級c言語やさしいC」という本を1冊一通りやりました。
宜しくお願い致します。

コード:

#include<stdio.h>
#include<string.h>

int MENU(int kensu);					//メニューの表示
int Bytecheck(int bytesu);				//バイトチェックを行う


struct ADDRESS_DATA		//構造体型の定義
{
	char name[50];
	char jusho[50];
	char denwa[50];
};


int main(void)
{
	struct ADDRESS_DATA address[100];		//構造体変数の宣言

	int i=1,kensu=0,j=0,bytesu,Nerror=0;
	char name[50],jusho[50],denwa[50],x,m;

	do{
		m = MENU(kensu);
//登録
		if(m == '1'){

			printf("名前の入力>>");
			scanf("%s",&name[j]);
			bytesu = strlen(&name[j]);			//バイト数をstrlen関数で数える
			Bytecheck(bytesu);					//バイト数のチェックをする
			if(Nerror == -1){					//入力エラーがあったらcontinueで抜けてメニューを表示する
				continue;
			}
			rewind(stdin);

			printf("住所の入力>>");
			scanf("%s",&jusho[j]);
			bytesu = strlen(&jusho[j]);
			Bytecheck(bytesu);
			if(Nerror == -1){
				continue;
			}
			rewind(stdin);

			printf("電話番号の入力>>");
			scanf("%s",&denwa[j]);
			bytesu = strlen(&denwa[j]);
			Bytecheck(bytesu);
			if(Nerror == -1){
				continue;
			}
			rewind(stdin);

			j++;

			printf("これで登録しますか?\n");
			printf("  Y  or  N  \n");
			scanf("%c",&x);
			if(x == 'Y' || x == 'y'){
				strcpy(address[i].name,name);		//strcpyで文字列をアドレス帳にコピーしていく
				strcpy(address[i].jusho,jusho);
				strcpy(address[i].denwa,denwa);
				i++;
				kensu++;
			}
		}
//表示
		if(m == '2'){
			printf("現在の登録件数は「%d」です。\n\n",kensu);
			for(i=1;i<=kensu;i++){
				printf("No.%d\n",i);
				printf("名前    :%s\n",address[i].name);
				printf("住所    :%s\n",address[i].jusho);
				printf("電話番号:%s\n",address[i].denwa);
			}
			
		}
//終了
		if(m == 'Q' || 'q'){
			break;
		}
	}while(kensu < 10);

	return 0;
}


//メニューを表示するプログラム
int MENU(int kensu)
{
	char m;

	printf("アドレス帳システム		");
	printf("登録件数:%03d件\n\n",kensu);
	printf("1.登録\n");
	printf("2.表示\n");
	printf("Q.終了\n");
	printf(">");
	scanf("%c",&m);

	return m;
}


//バイト数をチェックするプログラム
int Bytecheck(int bytesu)
{
	int Nerror=0;

	if(bytesu > 50){					//バイト数が50以上なら入力エラーを返す
		printf("入力エラー\n");
		Nerror = -1;
	}
	
	return Nerror;						//Nerrorを返す
}

添付ファイル
アドレス帳要件.docx
(101.19 KiB) ダウンロード数: 79 回

box
記事: 2002
登録日時: 13年前

Re: アドレス帳を作っています。

#2

投稿記事 by box » 2年前

コード:

		Bytecheck(bytesu);					//バイト数のチェックをする
		Bytecheck(bytesu);
		Bytecheck(bytesu);
戻り値を捨てているのはどうしてですか?

コード:

    Nerror = Bytecheck(bytesu);
のようにして、戻り値をきちんと受け取らないとまずくないですか?
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

box
記事: 2002
登録日時: 13年前

Re: アドレス帳を作っています。

#3

投稿記事 by box » 2年前

えっと、まさか、
main()のNerror

Bytecheck()のNerror

同じだとは思ってませんよね?
両者は、ともにその関数内のローカル変数ですので、
別々の場所(アドレス)にありますよ。名前は同じですが、ひも付けはありません。

あと、なんでもかんでもmain()に押し込んでいるのが気になります。
main()は司令塔の役割を果たすだけにして、
データ入力用の関数とか表示用の関数とかに分割する方が
プログラム全体の見通しがよくなると思うんですけど。
仮に自分が質問者さんの上司か何かなら、
「設計やり直し!」
って言うところです。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

ソラ
記事: 4
登録日時: 2年前

Re: アドレス帳を作っています。

#4

投稿記事 by ソラ » 2年前

#2と#3のboxさん
回答ありがとうございます。
#2は理解できました。ありがとうございます。
#3のほうなのですが、一度、登録をするだけの関数と表示だけする関数を作ったのですが
構造体の方にうまく格納できてなかったのでとりあえず要件を満たす方が先かなと思いmain関数に入れてます。

そして、もう一つ質問ですがアドレス(名前、住所、電話番号)を登録した後にメニューを出したいので、continueを追加したのですが、メニューは出たのですがキーを何も押せずにプログラムが終わってしまいます。
分かりにくかったら新しい質問として部屋を立てます。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: アドレス帳を作っています。

#5

投稿記事 by みけCAT » 2年前

ソラ さんが書きました:
2年前
メニューは出たのですがキーを何も押せずにプログラムが終わってしまいます。
ソラ さんが書きました:
2年前

コード:

//終了
		if(m == 'Q' || 'q'){
			break;
		}
この条件式は、「m == 'Q'」と「'q'」のうち1個以上が真である(0でない)とき、真になります。
'q' の値は(少なくともShift_JISおよびUTF-8では)113であり、0ではないので、これは真になります。
よって、mの値にかかわらず、条件は真となり、プログラムが終了します。

コード:

if(m == 'Q' || m == 'q'){
とするとよいでしょう。

また、名前、住所、電話番号をそれぞれ &name[j]、&jusho[j]、&denwa[j] に読み込んでいるのも不適切です。
これでは、jの値が大きくなった時、それぞれ最初の部分は前に入力されたデータを残し、
途中から読み込むことになってしまいます。
それぞれname、jusho、denwaとするべきです。
こうすることで、登録する時にはそれぞれname、jusho、denwaからコピーしていることとも整合性がとれます。
オフトピック
入力の長さの制限が無いので、
長過ぎる入力を入れられると異常終了などを起こす可能性がある危険なコードになっていますが、
まあこれへの対処は余裕があればということで…
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ソラ
記事: 4
登録日時: 2年前

Re: アドレス帳を作っています。

#6

投稿記事 by ソラ » 2年前

みけCATさん 回答ありがとうございます。

コード:

if(m == 'Q' || m == 'q'){
としたらプログラムが続くようになりました。
ありがとうございます!

コード:

F:\C言語\Addresschou>Addresschou
アドレス帳システム              登録件数:000件

1.登録
2.表示
Q.終了
>1
名前の入力>>a
住所の入力>>a
電話番号の入力>>a
これで登録しますか?
  Y  or  N
y
アドレス帳システム              登録件数:001件

1.登録
2.表示
Q.終了
>アドレス帳システム             登録件数:001件

1.登録
2.表示
Q.終了
>q
そして、次の問題なのですが
上のようにメニューが1回余分に出てくるのですがこれに関しては見当もつきません。
原因がわかりますでしょうか?

あと、アドレス帳要件の
「1.何も入力せずにリターンキーを入力した場合はメニューに戻る。」
の部分の実装の仕方わかりません。

宜しくお願いします。

ソラ
記事: 4
登録日時: 2年前

Re: アドレス帳を作っています。

#7

投稿記事 by ソラ » 2年前

自己解決しました!

コード:

#include<stdio.h>
#include<string.h>

int MENU(int kensu);					//メニューの表示
int Bytecheck(int bytesu);				//バイトチェックを行う


struct ADDRESS_DATA		//構造体型の定義
{
	char name[50];
	char jusho[50];
	char denwa[50];
};

#define MAX_KENSU 10							//最大件数の定義

int main(void)
{
	struct ADDRESS_DATA address[100];			//構造体変数の宣言

	int i=1,kensu=0,bytesu,Nerror=0;
	char name[256],jusho[256],denwa[256],x,m;


	while(1){									//'Q'か'q'を入力すれば抜けれるので永久ループにする
		m = MENU(kensu);						//メニューの表示

//登録
		if(m == '1'){
			rewind(stdin);						//入力エラーがあった場合前回入力した文字列のリセット
			printf("名前の入力  >>");
			gets_s(name,sizeof(name));			//gets_sでスペースにも対応
			if(name[0] == '\0'){				//name[0]に'\0'が入っていたらcontinueで抜けてメニューを表示する
				continue;
			}
			bytesu = strlen(name);				//バイト数をstrlen関数で数える
			Nerror = Bytecheck(bytesu);			//バイト数のチェックをする
			if(Nerror == -1){					//入力エラーがあったらcontinueで抜けてメニューを表示する
				rewind(stdin);
				continue;
			}
			rewind(stdin);

			printf("住所の入力  >>");
			gets_s(jusho,sizeof(jusho));
			if(name[0] == '\0'){
				continue;
			}
			bytesu = strlen(jusho);
			Nerror = Bytecheck(bytesu);
			if(Nerror == -1){
				continue;
			}
			rewind(stdin);

			printf("電話番号の入力>>");
			gets_s(denwa,sizeof(denwa));
			if(name[0] == '\0'){
				continue;
			}
			bytesu = strlen(denwa);
			Nerror = Bytecheck(bytesu);
			if(Nerror == -1){
				continue;
			}
			rewind(stdin);

			printf("これで登録しますか?\n");
			printf("  Y  or  N  \n");
			scanf("%c",&x);
			if(x == 'Y' || x == 'y'){
				if(kensu < MAX_KENSU){					//件数が最大件数より少ない場合
					strcpy(address[i].name,name);		//strcpyで文字列をアドレス帳にコピーしていく
					strcpy(address[i].jusho,jusho);
					strcpy(address[i].denwa,denwa);
					i++;
					kensu++;
				}else{									//最大件数を超えたらエラーを出しメニューを表示する
					printf("\n!!!!!!!最大件数エラー!!!!!!!\n");
					printf("これ以上は登録できません!\n");
				}
			}
		}
//表示
		if(m == '2'){
			printf("現在の登録件数は「%d」です。\n\n",kensu);
			for(i=1;i<=kensu;i++){
				printf("No.%d\n",i);
				printf("名前    :%s\n",address[i].name);
				printf("住所    :%s\n",address[i].jusho);
				printf("電話番号:%s\n\n",address[i].denwa);
			}
		}
//終了
		if(m == 'Q' || m == 'q'){
			break;
		}
	}

	return 0;
}


//メニューを表示するプログラム
int MENU(int kensu)
{
	char m;
	rewind(stdin);							//これを入れないと余分にメニューが出てしまう
	printf("アドレス帳システム		");
	printf("登録件数:%03d件\n\n",kensu);
	printf("1.登録\n");
	printf("2.表示\n");
	printf("Q.終了\n");
	printf(">");
	scanf("%c",&m);

	return m;
}


//バイト数をチェックするプログラム
int Bytecheck(int bytesu)
{
	int Nerror=0;

	if(bytesu > 50){					//バイト数が50以上なら入力エラーを返す
		printf("入力エラー\n");
		Nerror = -1;
	}
	
	return Nerror;						//Nerrorを返す
}
メニューが余分に出てきてしまう現象ですが、メニュー表示プログラムの最初に'rewind(stdin)'を入れたらなくなりました。  //これに関しては自分でも理由があんまり理解ができてないです。わかる方がいましたら説明をしていただきたいです。

リターンキーを押した場合にメニューに戻る機能ですが、
gets_sを使い、リターンキーが押されたときに'\0'を返すのでifで一文字目が'\0'かどうか判定します。

という感じでアドレス帳が一応動くようになりました。

ご回答いただいたお二方、大変ありがとうございました!

上にプログラムを載せておくので困っている方がいましたらご活用ください。

返信

“C言語何でも質問掲示板” へ戻る