ページ 11

質問です。

Posted: 2008年4月15日(火) 23:39
by ボムボム
入力された2つの整数の差を絶対値を出力するプログラム

課題で、入力された2つの整数の差を絶対値を出力するプログラムをつくりました.
整数には、負数を入力される場合も考慮し,入力される数値は、正数の場合は最大8桁まで有効とし、9桁目以降を無視、負数の場合は '-'を含め、最大9桁まで有効とし,10桁目以降を無視します。
今日指導員に見せたところ、「入力した数値を保存する必要はない」、「a[ i ] = atoi( n[ i ] ) は使わないように」、「scanf,get,do-whileを使わないように」等の指導を受けました。
どのように直したらよいか教えてください. お願いします.
下記のプログラムには多々エラーがありますがご指導宜しくお願い致します。
私は自宅ではCPad、会社ではTeraPadを使っています。
#include <stdio.h>
#include <stdlib.h>

#define SIZE 11
#define TASU_MAX 8
#define HIKU_MAX 9
#define C_NULL ('\0')
#define RC_OK 0 /*入力OK*/
#define RC_ND -1 /*入力データなし*/
#define RC_ID -2 /*無効な入力データ*/
#define RC_MD -3 /*負のデータ*/

int absolute( int n1, int n2 ) ;

int main(void)
{
	char n[ 2 ][ SIZE ];
	int i ; 		/*文字入力回数のカウント*/
	int a[ 2 ] ; 	/*整数データ*/
	int ab ; 		/*絶対値*/
	int count ;		/*入力文字個数*/
	int n_pos ;		/*null付与位置*/
	char c ; 		/*入力文字*/


	for( i = 0 ; i < 2 ; i ++ )
	{	
		
		printf( "%d番目の数値を入力 :", i + 1 ) ;
		for( count = 0 ; count <= SIZE ; count ++ )
		{
			c = getchar() ;
		/*cが数字もしくは一文字目が-なら*/
			if( c >= '0' && c <= '9' || count == 0 && c == '-' )
			{
				n[ count ] = c ;
				continue ;
			}
		}
			/*改行なら*/
		if( c == '\n' )	break ;
		printf( "Error!! : 数値以外の入力\n" ) ;
		return( RC_ID ) ; 

	}	
		/*入力文字数が0なら*/
	if( count == 0 )
	{
		printf("Error!! : 改行のみまたは、'-'のみの入力\n");
		return( RC_ND );
	}
		/*入力が1文字のみかつ-入力なら*/
	else if( n[ i ][ 0 ] == '-' && count < 2 )
	{
		printf("Error!! : 改行のみまたは、'-'のみの入力\n");
		return( RC_ID ) ;
	}


		/*null付与位置の設定*/
	/*一文字目が-かつ9文字以上なら*/
	if( n[ i ][ 0 ] == '-' && count > HIKU_MAX )
	{
		n_pos = HIKU_MAX ;
	}
	else
	{
		/*8文字以上なら*/
		if( count > TASU_MAX )
		{
			n_pos = TASU_MAX ;
		}
		else
		{
			n_pos = count ;
		}
		}
		n[ i ][ n_pos ] = '\0' ;/*null付与*/
		a[ i ] = atoi( n[ i ] ) ;/*文字列を数字に変換*/

		
	}
	ab = absolute( a[ 0 ], a[ 1 ] ) ;
	printf( "%d と %dの差の絶対値は %d \n", a[ 0 ], a[ 1 ], ab ) ;
	return( RC_OK );
}


int absolute(int n1, int n2)
{
	int i ;

	i = n1 - n2 ;
	return i >= 0 ? i : -i ;
}

Re:質問です。

Posted: 2008年4月16日(水) 09:19
by toyo
scanfもdo-whileも使ってないようですが「使うように」の間違いかな?
scanfで入力するなら
#include <stdio.h>

int main(void)
{
	int number;

	printf("数値を入力 ");
	scanf("%9d", &number);
	while (number > 99999999 || number < -99999999)
	{
		number /= 10;
	}
	printf("number=%d\n", number);
	
	return 0;
}
とか

Re:質問です。

Posted: 2008年4月16日(水) 09:49
by バグ
scanfとdo~whileを使えるならば、こんな感じかな?
#include <stdio.h>
#include <stdlib.h>

void input_number(int* number, const char* comment)
{
	do
	{
		printf("%sを入力して下さい\n", comment);
		scanf("%9d", number);
	}
	while (*number > 99999999 || *number < -99999999);
}

int main(void)
{
	int num_1 = 0, num_2 = 0;

	// 数値の入力
	input_number(&num_1, "数値1");
	input_number(&num_2, "数値2");

	// 入力された数値の表示
	printf("\n");
	printf("数値1 = %d\n", num_1);
	printf("数値2 = %d\n", num_2);

	// 2つの整数の絶対値の算出
	printf("絶対値 = %d\n", abs(num_1 - num_2));

	return 0;
}

Re:質問です。

Posted: 2008年4月16日(水) 12:02
by ボムボム
表現が悪くて申し訳ありませんでした。
「使ってはいけない」というのは、この課題だけでなく研修全体のルールです。
なので返信して頂いたプログラムは使用できません。

Re:質問です。

Posted: 2008年4月16日(水) 12:18
by バグ
getを使ってはいけないという事は、getc、gets、getcharなども駄目なんですか?
同じように、scanfを使ってはいけないという事は、fscanf、sscanfなんかも駄目なんでしょうか?

Re:質問です。

Posted: 2008年4月16日(水) 12:29
by バグ
あ、ひょっとして、入力系関数の使用に制限が掛かっているという事は、コマンドライン引数で指定しないさいという事なのかもしれませんね♪

Re:質問です。

Posted: 2008年4月16日(水) 12:46
by 管理人
http://www.google.co.jp/search?hl=ja&q= ... %99%BA&lr=

この辺のことを理解させる為の課題なんですかね?
もしそうならリンク先の解決手段を用いて実装すればいいと思います。
もしコマンドライン引数で指定する場合は

http://www.geocities.jp/bananajuku/r_c/argv.html

この辺参考になると思います。

Re:質問です。

Posted: 2008年4月16日(水) 12:52
by 組木紙織
do-whileがだめだということはwhileやforをつかえということなのかな。
それともifとgoto。。。

Re:質問です。

Posted: 2008年4月16日(水) 12:58
by ボムボム
for while等を使っています。
gotoは使えないルールです。
fgetも使っていいと言われていますが使うと「getcharに直せ」と
注意されます。

Re:質問です。

Posted: 2008年4月16日(水) 13:13
by Mist
時期的に会社の新人研修かと思いますが、こういったところで答えを
求めるよりも自分で調べられるだけ調べて、それでもわからなかった
ら指導員に聞くほうがいいと思いますけどね。(その場合、自分がどこ
まで調べてどこがわからないのかを具体的に提示すること。ここの質
問に書かれたような内容ではOUTです)

新人研修というのはその会社で必要とする技術を身につける場でもあり
ますが、自分で調べる力やグループ作業をしていく上で必要な手順やマ
ナー等を学ぶステージでもあるから、ソースコードとして満点回答を出
すことにはあんまり意味がなかったりします。

と、研修指導したことのある人からのアドバイスです。

Re:質問です。

Posted: 2008年4月16日(水) 14:42
by バグ
うーん、かなり力技だけど、こんな感じ?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

// 文字列を入力する
// char* buf = 文字列格納用配列へのポインタ
// int size  = バッファのサイズ
void strinput(char* buf, int size)
{
	// カウント用変数
	int cnt, minus = 0;

	// 文字列の入力
	for (cnt = 0; cnt < size - 1; cnt++)
	{
		// アドレスをずらしながらバッファに代入していく
		*(buf + cnt) = (char)getchar();

		// 改行文字が入力されたらループを抜ける
		if (*(buf + cnt) == '\n')
		{
			break;
		}
	}

	// 終端文字を追加
	*(buf + size) = '\0';

	// 数字符号以外を'\0'にする
	for (cnt = 0; cnt < size; cnt++)
	{
		switch (*(buf + cnt))
		{
		case '-':
			if (cnt != 0)
			{
				// 1文字目以外でマイナス符号が見つかった
				minus++;
			}
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			// 1文字目以外でマイナス符号が見つかっていたら、それ以降の文字は全て無効とする
			if (minus != 0)
			{
				*(buf + cnt) = '\0';
			}
			break;
		default:
			// マイナス符号と数字以外は無効
			*(buf + cnt) = '\0';
			break;
		}
	}

	// ストリームのフラッシュ処理
	fflush(stdin);
}

int convert(int val, int cnt, int size)
{
	return (int)pow(10.0, (double)(size - cnt - 1)) * val;
}

// 文字列をint型の数値に変換する
// char* str = 変換したい文字列
int strint(char* str)
{
	int cnt, result = 0;

	// 1桁ずつ数値に変換していく
	for (cnt = 0; cnt < (int)strlen(str); cnt++)
	{
		switch (*(str + cnt))
		{
		case '-':
			break;
		case '0':
			break;
		case '1':
			result += convert(1, cnt, (int)strlen(str));
			break;
		case '2':
			result += convert(2, cnt, (int)strlen(str));
			break;
		case '3':
			result += convert(3, cnt, (int)strlen(str));
			break;
		case '4':
			result += convert(4, cnt, (int)strlen(str));
			break;
		case '5':
			result += convert(5, cnt, (int)strlen(str));
			break;
		case '6':
			result += convert(6, cnt, (int)strlen(str));
			break;
		case '7':
			result += convert(7, cnt, (int)strlen(str));
			break;
		case '8':
			result += convert(8, cnt, (int)strlen(str));
			break;
		case '9':
			result += convert(9, cnt, (int)strlen(str));
			break;
		default:
			if (*str == '-')
			{
				return result *= -1;
			}
			else
			{
				return result;
			}
		}
	}

	if (*str == '-')
	{
		return result *= -1;
	}
	else
	{
		return result;
	}
}

int main()
{
	int num_1 = 0, num_2 = 0;
	char str[10];

	// 数値1の入力
	printf("数値1を入力して下さい\n");
	strinput(str, 10);
	num_1 = strint(str);

	// 数値2の入力
	printf("数値2を入力して下さい\n");
	strinput(str, 10);
	num_2 = strint(str);

	// 数値と絶対値の表示
	printf("数値1 = %d\n", num_1);
	printf("数値2 = %d\n", num_2);
	printf("絶対値 = %d\n", abs(num_1 - num_2));

	// 終了
	return 0;
}

Re:質問です。

Posted: 2008年4月16日(水) 14:48
by やそ
>fgetも使っていいと言われていますが使うと「getcharに直せ」と注意されます。

scanf()の危険性を考慮して使わせないのは悪いことではありませんが、getchar()も同様な危険性があります。
以下、別サイトの抜粋文
----------------------------------------
getchar() はキーボードから 1 文字取得する関数ですが、 Windows や Unix ではキーボードから 1 文字入れただけでは getchar() から帰ってきません。1 文字入力した後に「リターン」を入れる必要があります。しかし、getchar() から帰って来た時点で「リターン」は入力用のバッファに残ったままであり、次に getchar() を呼ぶと、残っていた「リターン」を入れて帰ってきます。このように getchar() はバグを誘発しそうな危険な関数なので、i = getchar() あるいは scanf("%c",&c) などはお勧めしません。
----------------------------------------
getchar()は1文字入力用ですし、1文字入力するたびにリターンキーが必要となります。コレでは現実的なプログラムの作成は難しい(面倒くさい)と思われます。

getchar()で作った場合、入力イメージは(-123456、654321の場合)
-
1
2
3
4
5
6

6
5
4
3
2
1
と入力させるわけですよね。現実的な入力方法じゃないです^^;

たとえば、
 1.スタート
 2.終了
 3.つづきから
などのメニューがあって、番号を選択する場合などにはgetchar()も使えると思うのですがね^^;

fgets()とsscanfの組み合わせで入力させるのがいいんじゃないかな?
標準入力のstdinを指定すればキーボードから入力できるし^^

sscanfをする場合、事前にfets()で読み取った文字列の妥当性をチェックしたほうが良いでしょう。
あくまで文字列なので、どんな入力の仕方でも受け付けますから^^;
1.空エンター
2.桁数制限越え
3.マイナスの位置や数の妥当性
  →123-、--2345、1-234などなど
4.数字以外の文字

とかでしょうか。
それらを踏まえて、もう一度挑戦してみて下さいね^^

Re:質問です。

Posted: 2008年4月16日(水) 14:54
by やそ
バグさんお疲れ様です^^
かなりの力技、恐れ入ります^^;

指導員から課題を与えられて~というボムボムさんのレベルで、正直そこまで求められているのか??
と言う疑問は置いといて(笑)

コレを提出したときの指導員の顔を見てみたくなるのは私だけ??(だいたひかる風)

Re:質問です。

Posted: 2008年4月16日(水) 15:02
by バグ
>>やそさん
オーバーフローチェックをしてなかったり、fflushが本来の使い方でなかったりと、かなり突っ込みどころ満載のソースではありますが、とりあえず私の環境では問題なく動きました(笑)
ちなみにWindowsXP、VC++6.0で開発してます。

なんというか、ポインタや文字列の扱いに慣れるという意味では、面白い課題ではないかな~?と思いますね(^-^)

Re:質問です。

Posted: 2008年4月16日(水) 16:17
by やそ
>>バグさんへ
ボムボムさんは絶対値計算を自作していますね。
もしかしたらabs()も使用禁止??
case '1':
			result += convert(1, cnt, (int)strlen(str));
			break;
の方法にはこだわりを感じました(笑)
また、char str[10];
を使いまわすことによって、「入力した数値を保存する必要はない」の要件を満たしていますね。
ある程度なれた方なら当たり前にやっていることではありますが、初心者などは、ロジックの分かりやすさから
2つの変数用に入力バッファも分けて用意したりすることがありますからね。
指導員の言いたいこと(狙い)もその辺なのでしょうね^^

Re:質問です。

Posted: 2008年4月16日(水) 18:28
by ボムボム
申し訳ありません。
int absolute(int n1, int n2)
{
int i ;

i = n1 - n2 ;
return i >= 0 ? i : -i ;
}

int absolute(int n1, int n2)は使わないといけないユーザー関数なのでこれがないと提出できません。

Re:質問です。

Posted: 2008年4月16日(水) 18:55
by やそ
>int absolute(int n1, int n2)は使わないといけないユーザー関数なのでこれがないと提出できません。

やはりそうでしたか。
ボムボムさんに質問です。いまさらですが、10進数8桁の正数はご使用になられているコンパイラのintの制限内に収まっていますでしょうか?
収まっていない場合、誤差が出ますよね?(何誤差だったか忘れました。丸め?情報落ち?桁落ち?)
またabsolute()の引数もint?

Re:質問です。

Posted: 2008年4月16日(水) 19:12
by バグ
>>ボムボムさん
私の書いたソースの中で絶対値を計算している場所があります。そこで使用している関数と、その自前のabsolute関数を入れ替えていただくだけで動くように思います。

あと、やそさんも書かれていましたが、getchar関数の動きは環境によって異なります。私はVisualStudioでの開発でしたので、それにあわせた組み方をしています。何が言いたいかというと、ボムボムさんの環境でも同じように動いてくれる保障は無いという事なんです。

その辺りの確認作業まではこちらではアドバイスのしようがありませんので、ご自分でお願いしますね。

Re:質問です。

Posted: 2008年4月16日(水) 19:26
by ボムボム
わかりました。
皆さん色々と有難う御座いました。
お世話になりました。

Re:質問です。

Posted: 2008年4月17日(木) 01:32
by しっぽ
/*
久しぶりの投稿です。投稿したコードは、処理順が変テコだったり、
可読性が悪かったりしてますが、短いコードとの折り合いを
もちべーじょん にして作ってみました。
*/

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

#define KETA_MAX 9 // 符号も1桁に数えて、8+1=9
#define ERRSTRLEN 256

int absolute(int n1, int n2)
{
	int value = n1 - n2;
	return value > 0 ? value : -value;
}

int main()
{
	int j, k;
	int ch;
	int sign;		// 符号
	int value[2];	// 入力数値
	char err[ERRSTRLEN] = {0};

	for(j = 0 ; j < 2 ; j++){

		printf("%d番目の数値を入力 :", j + 1) ;

		for(k = 0, value[j] = 0, sign = 1; (ch = getchar()) != '\n'; ++k){

			if(k == 0){
				if(ch == '-')	sign = -1;
				if(ch == '-' || ch == '+')	continue;
				++k; // 符号も1桁として数えるので+1する
			}

			value[j] = value[j] * 10 + ch - '0';

			if(k >= KETA_MAX)		strncpy(err, "桁数がオーバーした",     ERRSTRLEN - 1);
			else if(ch < '0' || ch > '9')	strncpy(err, "数値以外の入力があった", ERRSTRLEN - 1);
		}

		if(k < 2)     strncpy(err, "数値入力がなかった", ERRSTRLEN - 1);
		if(err[0])    break;

		value[j] *= sign;
	}

	if(err[0])	return printf("エラー : %s", err) != 0;

	return !printf("差分の絶対値 : %d\n", absolute(value[0], value[1]));
}