グローバル変数

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
カナカナ

グローバル変数

#1

投稿記事 by カナカナ » 9年前

このプログラムをグローバル変数なしで同じ動作をするプログラムを作りたいのですがどこをどのように変えたらよいでしょうか?

コード:

#include <stdio.h> 
#include <malloc.h> 
#include <stdlib.h> 
#include <conio.h> 
#include <string.h> 
 
enum Command{ADD=1, SUMMARY, DISPLAY, EDIT, SAVE, LOAD, DELETE, END=9};
 
#define MAX_NAME_SIZE	30
#define MAX_TEL_SIZE	15
 
#define SUCCESS			0
#define ERROR_MEMORY	1
#define ERROR_FILE_OPEN	2
#define ERROR_KEYBOARD	3
#define ERROR_NO_DATA	4
 
typedef struct person
{
	int  number;				// 管理番号
	char name[MAX_NAME_SIZE];	// 名前
	char *address;				// 住所
	char tel[MAX_TEL_SIZE];		// 電話番号
	int  age;					// 年齢
 
	struct person *pnext;	// 次のデータを指す構造体ポインタ
	struct person *pprev;	// 前のデータを指す構造体ポインタ
 
}PERSON,*PPERSON;
 
PPERSON pLastPerson=NULL;		// 最後に登録した構造体を指すポインタ
PPERSON pFirstPerson=NULL;	// 最初に登録した構造体を指すポインタ
int count=0;					// 現在何個のデータが登録されているかをカウント
 
int AddData();
int DisplaySummary();
int DisplayData();
int EditData();
int SaveData();
int LoadData();
int DeleteData();
void DisplayError(int ErrorCode);
void stop_func();
 
//
//	機能	 :	main関数
//
//	返り値	 :	なし(void 型)
//
//	機能説明 :	
//
//	備考	 :	
//
int main(void)
{
	char szInput[256];	// メニューとして入力される文字列
	int nCommand;		// コマンド判定用変数
	int ret;			// エラー判定
	
	while(1)
	{
		// スタートメニューのインターフェイス
		printf("\n                           住所録作成プログラム\n\n\n");
		printf("  スタートメニュー\n");
		printf("  ----------------------------------------------\n");
		printf("  1)データの追加\n");
		printf("  2)一覧表示\n");
		printf("  3)個別表示\n");
		printf("  4)編集作業\n");
		printf("  5)データセーブ\n");
		printf("  6)データロード\n");
		printf("  7)データ削除\n");
		printf("  9)プログラムの終了\n");
		printf("  ----------------------------------------------\n");
		printf("\n\n\n\n\n\n\n\n\n");	
		printf("  メニュー番号入力→");
 
		stop_func();
 
		// コマンド入力
		gets(szInput);
		nCommand=atoi(szInput);
 
		// コマンド判定
		switch (nCommand)
		{
			case ADD:		// 追加関数へ
				ret=AddData();
				break;
			case SUMMARY:	// 一覧表示関数へ
				ret=DisplaySummary();
				break;
			case DISPLAY:	// 個別表示関数へ
				ret=DisplayData();
				break;
			case EDIT:		// 編集関数へ
				ret=EditData();
				break;
			case SAVE:		// セーブ関数へ
				ret=SaveData();
				break;
			case LOAD:		// ロード関数へ
				ret=LoadData();
				break;
			case DELETE:	// 削除関数へ
				ret=DeleteData();
				break;
			case END:		// プログラム終了
				ret=END;
				break;
			default:
				printf("\nメニュー番号を入れて下さい\n");
				stop_func();
				break;
		}
		printf("\n\n");
		// プログラム終了
		if(ret == END)
		{
			break;
		}
		// エラーを表示するかを判定
		if(ret != SUCCESS)
		{
			DisplayError(ret);
			stop_func();
		}
		fflush(stdin);
	}				
	return SUCCESS;
}
 
//
//	機能	 :	データ追加
//
//	返り値	 :	(int 型)
//
//	機能説明 :	
//
//	備考	 :	
//
int AddData()
{
	char age[10];		// 年齢格納配列
	char address[256];	// 住所格納配列
	int length;
	PPERSON pPerson;	// 構造体ポインタの宣言
 
	count++;
	pPerson=(PPERSON)malloc(sizeof(PERSON));
	if(pPerson == NULL)
	{
		return (ERROR_MEMORY);
	}
 
	// もしカウントが1なら = 最初に登録されるデータなら
	if(count == 1)
	{
		pFirstPerson=pPerson; 
	}
 
	pPerson->number=count;
	printf("年齢の入力→");
	if(fgets(age, 10, stdin) == NULL)
	{
		return (ERROR_KEYBOARD);
	}
	pPerson->age=atoi(age);
	fflush(stdin);
 
	printf("名前の入力→");
	if(fgets(pPerson->name, 30, stdin) == NULL)
	{
		return (ERROR_KEYBOARD);
	}
	fflush(stdin);
 
	printf("住所の入力→");
	if(fgets(address, 256, stdin) == NULL)
	{
		return (ERROR_KEYBOARD);
	}
	fflush(stdin);
 
	length=strlen(address);
	pPerson->address=(char*)malloc(length + 1);
	if(pPerson->address == NULL)
	{
		return (ERROR_MEMORY);
	}
	strcpy(pPerson->address, address);
 
	printf("電話番号の入力→");
	if(fgets(pPerson->tel, 15, stdin) == NULL)
	{
		return (ERROR_KEYBOARD);
	}
	fflush(stdin);
 
	// 最後に登録されるデータの次はないのでpnextにはヌルが入る
	pPerson->pnext=NULL;
 
	if(count != 1)
	{
		pLastPerson->pnext=pPerson;
		pPerson->pprev=pLastPerson;
	}
 
	// 最新のデータ(最後に登録されたデータ)のアドレスは
	// 常にわかっていなければならないのでグローバルのpLastPerson
	// に代入しておく
	pLastPerson=pPerson;
 
	return 0;
}
 
//
//	機能	 :	データ一覧表示
//
//	返り値	 :	(int 型)
//
//	機能説明 :	
//
//	備考	 :	
//
int DisplaySummary()
{
	PERSON *start;
	printf("データ番号  年齢  名前\n");
 
	//pFirstPersonすなわち最初に登録されたデータから
    //pnextをたどってpnextがnullになるまで
	//すなわち最後に登録されたデータまでを表示
	for(start=pFirstPerson; start != NULL; start=start->pnext)
	{
		printf("    %03d    %3d歳  %s",start->number,start->age,start->name);
	}
 
	printf("\n何かキーを押してください\n");
	stop_func();
 
	return 0;
}
 
 
//
//	機能	 :	データ個別表示
//
//	返り値	 :	(int 型)
//
//	機能説明 :	
//
//	備考	 :	
//
int DisplayData()
{
	PERSON *pdata;
	int i,j;
	char number[5];	// データ番号格納配列
 
	printf("表示したいデータ番号を入力してください→");
	if(fgets(number, 5, stdin) == NULL)
	{
		return (ERROR_KEYBOARD);
	}
	j=atoi(number);
	fflush(stdin);
 
	if(count == 0 || j == 0)
	{
		return (ERROR_NO_DATA);
	}
	if(j > count)
	{
		return (ERROR_NO_DATA);
	}
	
	pdata=pFirstPerson;
	for(i=1 ; i < j ; ++i)
	{
		pdata=pdata->pnext;
	}
 
	if(pdata == NULL)
	{
		return (ERROR_NO_DATA);
	}
	
	printf("\nデータ番号%d\n", pdata->number);
	printf("  年齢  %d歳\n", pdata->age);
	printf("  名前  %s", pdata->name);
	printf("  住所  %s", pdata->address);
	printf("  電話  %s", pdata->tel);
 
	printf("\n何かキーを押してください\n");
	stop_func();
 
	return 0;
}
 
//
//	機能	 :	データ編集
//
//	返り値	 :	(int 型)
//
//	機能説明 :	
//
//	備考	 :	
//
int EditData()
{
	PERSON *pedit;
	char judgment;
	char age[5];
	char number[5];
	char address[256];
	int i,j;
	int EditFlag=0;
	int length;
 
	printf("編集したいデータ番号を入力してください→");
	if(fgets(number, 5, stdin) == NULL)
	{
		return (ERROR_KEYBOARD);
	}
	j=atoi(number);
	fflush(stdin);
 
	if(count == 0 || j == 0)
	{
		return (ERROR_NO_DATA);
	}
 
	if(j > count)
	{
		return (ERROR_NO_DATA);
	}
 
	pedit=pLastPerson;
	for(i=count ; i > j ; --i)
	{
		pedit=pedit->pnext;
	}
 
	printf("\nデータ番号%d\n",pedit->number);
	printf("\n  年齢  %d歳\n",pedit->age);
	printf("  名前  %s",pedit->name);
	printf("  住所  %s",pedit->address);
	printf("  電話  %s",pedit->tel);
 
	while(1)
	{
		printf("\n  上のデータを編集しますか?(y/n)→");
		scanf("%c",&judgment);
 
		switch(judgment)
		{
			case 'y':
			case 'Y':
				EditFlag=1;
				break;
			case 'n':
			case 'N':
				return 0;
				break;
			default:
				printf(" yかnを入れて下さい\n");
				break;
		}
		fflush(stdin);
		if(EditFlag)
		{
			break;
		}
	}
 
	printf("年齢の入力→");
	if(fgets(age, 10, stdin) == NULL)
	{
		return (ERROR_KEYBOARD);
	}
	pedit->age=atoi(age);
	fflush(stdin);
 
	printf("名前の入力→");
	if(fgets(pedit->name, 30, stdin) == NULL)
	{
		return (ERROR_KEYBOARD);
	}
	fflush(stdin);
 
	printf("住所の入力→");
	if(fgets(address, 256, stdin) == NULL)
	{
		return (ERROR_KEYBOARD);
	}
	fflush(stdin);
 
	length=strlen(address);
	pedit->address=(char*)malloc(length + 1);
	if(pedit->address == NULL)
	{
		return (ERROR_MEMORY);
	}
	strcpy(pedit->address, address);
 
	printf("電話番号の入力→");
	if(fgets(pedit->tel, 15, stdin) == NULL)
	{
		return (ERROR_KEYBOARD);
	}
	fflush(stdin);
 
	return 0;
}
 
//
//	機能	 :	データセーブ
//
//	返り値	 :	(int 型)
//
//	機能説明 :	
//
//	備考	 :	
//
int SaveData()
{
	char judgment;
	char fname[64];
	PERSON *psave;
	FILE *fp;
 
	printf("セーブデータ名を入力→");
	gets(fname);
 
	if(fname == "\n")
	{
		printf("セーブデータ名を入れてください\n");
		return (-1);
	}
 
	if(count == 0)
	{
		return (ERROR_NO_DATA);
	}
 
	fp=fopen(fname,"r");
	if(fp)
	{
		printf("ファイル%sは存在します。上書きしますか?(y/n)→", fname);
		scanf("%c",&judgment);
 
		switch(judgment)
		{
			case 'y':
			case 'Y':
				break;
			case 'n':
			case 'N':
				fclose(fp);
				return 0;
				break;
			default:
				fclose(fp);
				return 0;
				break;
		}
		fflush(stdin);
		fclose(fp);
	}
 
	fp=fopen(fname,"w");
	if(fp == NULL)
	{
		return (ERROR_FILE_OPEN);
	}
 
	fprintf(fp,"%d\n",count);
 
	for(psave=pFirstPerson; psave != NULL; psave=psave->pnext)
	{
		fprintf(fp,"%d\n",psave->age);
		fprintf(fp,"%s",psave->name);
		fprintf(fp,"%s",psave->address);
		fprintf(fp,"%s",psave->tel);
	}
	fclose(fp);
 
	return 0;
}
 
//
//	機能	 :	データロード
//
//	返り値	 :	(int 型)
//
//	機能説明 :	
//
//	備考	 :	
//
int LoadData()
{
	PERSON *pload;
	FILE *fp;
	char fname[64];
	char address[256];
	int	DataNumber;
	int length;
 
	printf("ロードデータ名を入力→");
	gets(fname);	
 
	if(fname == '\0')
	{
		printf("セーブデータ名を入れてください\n");
		return (-1);
	}
 
	if(NULL == (fp=fopen(fname,"r")))
	{
		return (ERROR_FILE_OPEN);
	}
 
	fscanf(fp,"%d",&DataNumber);
 
	for(int i=0; i < DataNumber; ++i)
	{
		count++;
		pload=(PPERSON)malloc(sizeof(PERSON));
		if(NULL == pload)
		{
			return (ERROR_MEMORY);
		}
 
		if(count == 1)
		{
			pFirstPerson=pload;
			pFirstPerson->pprev=NULL;
		}
 
		pload->number=count;
 
		fscanf(fp,"%d",&(pload->age));
 
		fscanf(fp,"%s",pload->name);
		strcat(pload->name, "\n");
 
		fscanf(fp,"%s",address);
		length=strlen(address);
		pload->address=(char*)malloc(length + 1);
		if(pload->address == NULL)
		{
			return (ERROR_MEMORY);
		}
		strcpy(pload->address, address);
		strcat(pload->address, "\n");
 
		fscanf(fp, "%s", pload->tel);
		strcat(pload->tel, "\n");
 
		pload->pnext=NULL;
		if(count != 1)
		{
			pload->pprev=pLastPerson;
			pload->pprev->pnext=pload;
		}
		pLastPerson=pload;
	}
	
	fclose(fp);
 
	return 0;
}
 
//
//	機能	 :	データ削除
//
//	返り値	 :	(int 型)
//
//	機能説明 :	
//
//	備考	 :	
//
int DeleteData()
{
	PERSON *copy;
	PERSON *pdel;
	int i,j;
	char judgment;
	char number[5];
 
	printf("削除したいデータ番号を入力してください→");
	j=atoi(gets(number));
 
	if(count == 0)
	{
		return (ERROR_NO_DATA);
	}
	
	pdel=pFirstPerson;
	for(i=1 ; i < j ; ++i)
	{
		pdel=pdel->pnext;
	}
	
	printf("\nデータ番号%d\n",pdel->number);
	printf("\n  年齢  %3d歳\n",pdel->age);
	printf("  名前  %s\n",pdel->name);
	printf("  住所  %s\n",pdel->address);
	printf("  電話  %s\n",pdel->tel);
 
	printf("\n  上のデータを削除しますか?(y/n)→");
	scanf("%c",&judgment);
 
	switch(judgment)
	{
		case 'y':
		case 'Y':
			break;
		case 'n':
		case 'N':
			return 0;
		default:
			printf("yかnを入れて下さい\n");
			stop_func();
			return 0;
	}
	fflush(stdin);
 
	if(pdel == pFirstPerson)
	{
		pFirstPerson=pdel->pnext;
		if(count != 1)
		{
			pFirstPerson->pnext=pdel->pnext->pnext;
			pFirstPerson->pprev=NULL;
		}
 
		for(copy=pFirstPerson; copy != NULL ; copy=copy->pnext)
		{
			(copy->number)--;
		}
	}
	else if(pdel == pLastPerson)
	{
		pLastPerson=pdel->pprev;
		pLastPerson->pnext=NULL;
		pLastPerson->pprev=pdel->pprev->pprev;
 
	}
	else
	{
		pdel->pnext->pprev=pdel->pprev;
		pdel->pprev->pnext=pdel->pnext;
 
		for(copy=pdel->pnext; copy != NULL; copy=copy->pnext)
		{
			(copy->number)--;
		}
	}
	if(pdel->address)
	{
		free(pdel->address);
		pdel->address=NULL;
	}
	free(pdel);
	count--;
 
	return 0;
}
 
//
//	機能	 :	エラー表示
//
//	返り値	 :	(int 型)
//
//	機能説明 :	
//
//	備考	 :	
//
void DisplayError(int ErrorCode)
{
	switch(ErrorCode)
	{
	case ERROR_MEMORY:
		printf("メモリが確保できません\n");
		break;
	case ERROR_FILE_OPEN:
		printf("ファイルが開けません\n");
		break;
	case ERROR_KEYBOARD:
		printf("キー入力エラー\n");
		break;
	case ERROR_NO_DATA:
		printf("データがありません\n");
		break;
	default:
		break;
	}
 
	printf("\n何かキーを押してください\n");
	stop_func();
}
 
/* 画面を一時止める関数 */
void stop_func()
{
	int ch;
 
	do{
      ch=_getch();
	}while( ch >= 127 || ch < 8 );
}

アバター
ゆーずぃ
記事: 62
登録日時: 9年前
住所: 埼玉県

Re: グローバル変数

#2

投稿記事 by ゆーずぃ » 9年前

グローバル変数を使わない方法は、一般的に言って

1.ローカル変数を引数として渡す(必要に応じて戻り値で返す)
2.静的変数(static)にする。

があります。

今回の場合は引数として関数に渡していけばいいのではないでしょうか。
さすがに700行見るのはしんどいので具体的にここが、というのは示しませんが、
グローバル変数として使われているPERSON構造体変数2つと、countがどこで使われているのかのを
考え、引数としてうまくリレーできるような形を考えてみてください。

カナカナ

Re: グローバル変数

#3

投稿記事 by カナカナ » 9年前

返事が遅くなりました。

自分なりに言われた通り、ローカル変数を引数として渡して見たのですがうまくいきませんでした。

追加と一覧表示だけで良いので具体的に示していただけないでしょうか?

アバター
バグ
記事: 130
登録日時: 9年前
住所: 愛媛県
連絡を取る:

Re: グローバル変数

#4

投稿記事 by バグ » 9年前

どのようにうまく行かなかったのか、途中経過を貼り付けてみないと答えようがないとおもいますよ(^-^)

アバター
ゆーずぃ
記事: 62
登録日時: 9年前
住所: 埼玉県

Re: グローバル変数

#5

投稿記事 by ゆーずぃ » 9年前

そうですね、AddData()関数とDisplaySummary()関数だけでもいいので、
どんな風に考えて改造してみたのかを見せてもらえますか?
的外れでも、全然変わっていなくても構わないので^^

maru
記事: 150
登録日時: 9年前

Re: グローバル変数

#6

投稿記事 by maru » 9年前

このような場合、何も考えずにできる手法として、

1.グローバル変数をmain関数内に移動する。
2.コンパイルする(グローバル変数を使っている関数でエラーが発生)
3.エラーが発生した関数定義の仮引数にグローバル変数と同じ型、名前を追加。
4.コンパイルする(変更した関数の呼び出しでエラー発生)
5.エラーが発生した関数呼び出しに実引数を追加(元のグローバル変数名)
6.コンパイル(エラーが発生したら、3へ、発生しなければ終了)

という手が使えることがありますが、あまり推奨はいたしません。
ちゃんと設計すべきです(でも設計しても結果は同じような気が...)。

上記の内容を確認しながらやらないとC++ではデフォルト引数で痛い目にあることがあります。

カナカナ

Re: グローバル変数

#7

投稿記事 by カナカナ » 9年前

正直、

コード:

PPERSON pLastPerson=NULL;       // 最後に登録した構造体を指すポインタ
PPERSON pFirstPerson=NULL;  // 最初に登録した構造体を指すポインタ
int count=0;                    // 現在何個のデータが登録されているかをカウント
をAddData()関数とDisplaySummary()関数の中に入れたりしただけなんですが

maru
記事: 150
登録日時: 9年前

Re: グローバル変数

#8

投稿記事 by maru » 9年前

重要なことを忘れていました。
このような場合、何も考えずにできる手法として、

1.グローバル変数をmain関数内に移動する。
2.コンパイルする(グローバル変数を使っている関数でエラーが発生)
3.エラーが発生した関数定義の仮引数にグローバル変数と同じ型、名前を追加。
4.コンパイルする(変更した関数の呼び出しでエラー発生)
5.エラーが発生した関数呼び出しに実引数を追加(元のグローバル変数名)
6.コンパイル(エラーが発生したら、3へ、発生しなければ終了)

という手が使えることがありますが、あまり推奨はいたしません。
ちゃんと設計すべきです(でも設計しても結果は同じような気が...)。

上記の内容を確認しながらやらないとC++ではデフォルト引数で痛い目にあることがあります。
この方法でコンパイルは通るようになりますが、元のグローバルの値を書き換える関数では変数をポインタ渡しにして、関数呼び出し時にはポインタを取得して渡す必要があります。
/* 関数側 */
int func(int a){ /*関数定義*/ -> int func(int* pa){ /*型に'*'をつける*/
a = b;/*変数を使ったコード*/ -> *pa = b;/*ポインタに'*'をつける*/
}
/*関数呼び出し側*/
func(a);/*関数呼び出し*/ -> func(&a);/*ポインタを渡す関数には変数に'&'をつける*/

閉鎖

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