四則演算プログラムについて

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

四則演算プログラムについて

#1

投稿記事 by @とんぷぅ~ » 18年前

いつもお世話になっております@とんぷぅ~です。
今回も四則演算のプログラムなのですが、条件としては

①括弧を用いた計算を可能にする。
②スペースを入れなくても計算できるようにする。
③どんな整数を入力しても計算結果が正しく表示されるようにする。

というものなんですが、②については1 + 1 = 2を1+1=2でも計算
できるようにするということです。これはコマンドライン引数
から取得する方法でも可能なんでしょうか?

③については具体的に、桁の数を20や30にしても計算できるように
するということなんですが、これは型といったことを全く無視して
行うのでしょうか?どうすればよいのか全く思いつきません。

皆様よろしくお願いします。

管理人

Re:無題

#2

投稿記事 by 管理人 » 18年前

まず、四則演算を計算するには、スタックが有効ではないかと思いますが、スタックの利用方法についてはよろしいでしょうか?
(どこから解説すればよいかが変わってきますので)
あと、スタックで計算する場合、
1 + 1

1 1 +
になりますが、後者のままではよくないのでしょうか?

昔似たような投稿がありました。
http://www.play21.jp/board/formz.cgi?ac ... q&rln=2980
Justyさんのお書きになったサンプルは時間がたったため自動削除されてしまったようですが、
スタックを使った四則演算プログラムならすぐ作れます。

Justy

Re:無題

#3

投稿記事 by Justy » 18年前

# @とんぷぅ~さん
取得する方法でも可能なんでしょうか?
 問題なく可能です。
 複数のコマンドライン引数で与えられた計算式でも、1つの文字列に結合するか、
要素毎に分離するかしてしまえば楽に処理できるはずです。


どうすればよいのか全く思いつきません
 桁数が大きい数値の演算をするには、1つの手として多倍長整数を使うというのがあります。
 ググれば出てきますので、調べてみてください。



# 管理人 さん
時間がたったため自動削除
 懐かしいスレですね。
 あー、添付した物は過去ログ行きになると消えるんですね。
 消えてくれると嬉しい物もあったりするので、助かるといえば助かります(w

管理人

Re:無題

#4

投稿記事 by 管理人 » 18年前

LONGLONG型を使ってみるのもいいかと思います。
#include <stdio.h>
#include <windows.h>

int main(){
	int i;
	LONGLONG a=1;
	for(i=1;i<64;i++)
		printf("2^%-2d = %I64d\n",i,a*=2);
	return 0;
}

実行結果
2^1  = 2
2^2  = 4
2^3  = 8

・・・(略)

2^60 = 1152921504606846976
2^61 = 2305843009213693952
2^62 = 4611686018427387904
2^63 = -9223372036854775808
 
 

@とんぷぅ~

Re:無題

#5

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。レスありがとうございます。

申し訳ございませんが、スタックについては後入れ先出しの
データ構造ということしか理解しておりません。

11+というのは、引数に11+と入力し計算結果が2になる
ということなのでしょうか?

桁数についてはint型で扱うことが出来ない範囲も計算したい
(20桁どうしの掛け算など)のですが、文字列扱いにして
計算すれば良いのでしょうか?質問ばかりですみません。

@とんぷぅ~

Re:無題

#6

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。Justyさん。お世話になっております。
<<Justyさん
多倍長整数とは難しそうですが、調べてみます。
引数については結合か分離で可能なのですね。
ご回答ありがとうございました。

<<管理人さん
今回は一応何桁でも計算できるというのが目的で(あまり
長すぎてもアレなので、40桁まで扱いたいです。)
long long型だと扱える数値の数にどうしても、限りが
あるため、今回は使用出来ないのです。文章が分かりにくくて
すみませんでした。

管理人

Re:無題

#7

投稿記事 by 管理人 » 18年前

あら・・、学校でスタックを使ったサンプルを作ってたのに、忘れてて帰ってきてしまった(T_T
すみません、、、。
しかし今回の課題を見るとスタックを使った方法では実現出来そうにないですね。

1 + 1

1 1 +
と入力する事になりますが、スペースをなくすと11になってしまいますし。
http://www.google.co.jp/search?hl=ja&q= ... %83%89&lr=
この辺を参考に調べてみてください。

私はスタックを利用した四則演算しかパッと思いつかないのですが、
サンプルをもう一度作ってみます。

後は全てカッコや四則演算子を全て個別に一つずつ解析していく方法しか思いつきませんが、
それは賢くないですよね。。

box

Re:無題

#8

投稿記事 by box » 18年前

数値用のスタックと演算子(カッコを含む)用のスタックを
別々に用意すれば、逆ポーランド記法を使わなくても、
一般的な数式の書き方に対応できるように思います。

管理人

Re:無題

#9

投稿記事 by 管理人 » 18年前

スタックについてはご存知のようですので、そのまま
スタックを使った四則演算のサンプルを提示します。
以下のような計算式は
(1+1) * (2+2) / (4-2)
このように書きます。
1 1 + 2 2 + * 4 2 - /

実行計算過程はこのようになります。
1 1 + 2 2 + * 4 2 - /
データ: 1
データ: 1 1
データ: 2
データ: 2 2
データ: 2 2 2
データ: 2 4
データ: 8
データ: 8 4
データ: 8 4 2
データ: 8 2
データ: 4
    4

以下サンプルプログラムです。

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

#define RET    0
#define NUMBER 1
#define PLUS   2
#define MINUS  3
#define MULT   4
#define DIV    5
#define OTHER  6

#define stack_size 100

int stack[stack_size];
int sp;

void push(int x){//配列要素番号を1増やして引数を格納する。
   if (sp < stack_size-1)
      stack[++sp] = x;
   else {
      printf("スタックがいっぱい。 \n");
      exit(1);
   }
}

int pop(){//返り値に配列要素を返して配列要素番号を1減らす
   if (sp >= 0) 
      return stack[sp--];
   else {
      printf("スタック空っぽエラー.\n");
      exit(1);
   }
}

void print_stack(){
   int i;
   if (sp < 0) {
      printf("スタックがからっぽ\n");
      return;
   }
   printf("データ: ");
   for(i=0; i<=sp; i++)
      printf("%d ", stack);
   printf("\n");
}

int gettoken(int *num){
   int c, n;
   while ((c = getchar()) == ' ' || c == '\t');//スペースを読み飛ばす
   switch(c){
	  /*ここから入力文字列を数値に変換*/
      case '0': case '1': case '2': case '3': case '4':
      case '5': case '6': case '7': case '8': case '9':
         n = c - '0';//文字を数値に変換
         while(1) {//入力された文字列を数値に変換
            c = getchar();//1文字取得
            if ('0' <= c && c <= '9'){//0~9まで
               n = 10*n + c - '0';
            }
            else {
               break;
            }
         }
         *num = n;
	  /*ここまで*/
		 return NUMBER;

	  //読んでいる入力情報によって返り値を分岐
      case '+' : return PLUS;
      case '-' : return MINUS;
      case '*' : return MULT;
      case '/' : return DIV;
      case '\n': return RET;
      default  : return OTHER;
   }
}

int main(){
   int token, num, x;
   token = gettoken(&num);//numに数値を入れ、tokenに識別番号を入れる。
   sp =-1;	//sp初期化
   while(token != RET) {//tokenが改行で無い限り
      switch(token){
         case NUMBER: push(num); break;//数値ならプッシュ
         case PLUS  : push(pop() + pop());//プラスなら2つ取り出して取り出した2つを格納する。
                      break;
         case MINUS : x = pop(); 
                      push(pop() - x);
                      break;
         case MULT  : push(pop() * pop());
                      break;
         case DIV   : x = pop(); 
                      if (x != 0) {
                         push(pop() / x);
                         break;
                      }
                      else {
                         printf("0割り禁止\n");
                         exit(1);
                      }
         case OTHER : printf("不正な文字\n"); 
                      exit(1);
      }
      print_stack();
      token = gettoken(&num);
   }
   if (sp+1== 1)
      printf("%5d\n", pop());
   else {
      printf("シンタックスエラー\n"); 
      exit(1);
   }
   return 0;
}
 
 

@とんぷぅ~

Re:無題

#10

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。boxさんありがとうございました。

>後は全てカッコや四則演算子を全て個別に一つずつ解析していく方法しか
私も調べたところ、この方法が一番いいのかと思ったのですが、あまり賢い
方法ではないかもしれませんね。

>数値用のスタックと演算子(カッコを含む)用のスタックを別々に用意
そんな方法があるのですね。しかし難しそうですね。
スタックについてもまだまだ勉強しなければいけないですね。

>スタックを使った四則演算のサンプル
サンプルありがとうございました。動かしてみたんですが、やはり逆ポーランド
ではなく、一般的な表記法 1 + ( -5 ) = -4になるような方法じゃないと無理
みたいです。

ちなみに以前作成したサンプルです。関数化してない為見にくいと思いますが
これは整数型の四則演算を行うプログラムです。扱える範囲を2147483647~-2147483648としています。
これにベースに上記の3つの条件を付け足したプログラムを作成しろということなんですが、
これまではオーバーフローばかりを気にしていたのに、今度は全く逆に何桁
でも計算出来るようにすると言ったギャップにとまどっています(笑)
/****************************************	

*																	   *
		
* NAME    【sisokenzan.c】         * 

* SYNOPSIS【sisokenzan lop op rop】*

*																	   *				
	
*****************************************/

#pragma warning ( disable : 4996 )

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

int main ( int argc, char *argv[/url] )
{
	int max = INT_MAX;	 //	 表現可能な最大値
	int min = INT_MIN;	 //	 表現可能な最小値

	long long lop;		 //	 左のオペランドに入力する値
	long long rop;		 //  右のオペランドに入力する
	long long mod;		 //	 除算の余り
	long long result;	 //	 計算結果を格納

	char *check;		 //	 変換不可能な文字を格納
	char op;			 //  入力する演算子 

	//	引数の個数チェック
	if ( argc != 4 ) {
		printf ( "usage : %s lop(Left operand) op(Operator) rop(Right operand)\n", argv[0] );
		exit   ( 0 );
	}	
	
	//	引数を整数型に変換
	lop = atoi ( argv[1] );
	rop = atoi ( argv[3] );
	
	//	引数の取得(配列中の先頭文字)
	op = ( argv[2][0] );

	//	lopが整数であるか文字であるかのチェック  
	lop = strtol ( argv[1], &check, 10 );
	if ( errno != ERANGE ) {
		if ( *check != '\0' ) {
			printf ( "error : Please input the integer\n" );
			exit   ( 0 );
		}

	//	lopに範囲を超えた値が入力された場合	
	} else {
		printf ( "error : The range of the int type is exceeded\n" );
		exit   ( 0 );
	}

	//	ropが整数であるか文字であるかのチェック		
	rop = strtol ( argv[3], &check, 10 );
	if ( errno != ERANGE ) {
		if ( *check != '\0' ) {
			printf ( "error : Please input the integer\n" );
			exit   ( 0 );
		}

	//	lopに範囲を超えた値が入力された場合
	} else {
		printf ( "error : The range of the int type is exceeded\n" );
		exit   ( 0 );
	}
	if ( strlen ( argv[2] ) >= 2 ) {
		 printf ( "error : Please input the operator correctly\n" );
		 exit   ( 0 );
	}

	//	演算子ごとに計算  
	switch ( op ) {
		case '+' :
			result = lop + rop;
			break;
	
		case '-' :
			result = lop - rop;
			break;

		case '*' :
			result = lop * rop;
			break;

		case '/' :
			if ( rop == 0 ) {
				printf ( "error : 0 division is a prohibition\n" );
				exit   ( -1 );
			}

			//	除算のオーバーフローを考慮
			if ( ( lop == min ) && ( rop == -1 ) ) {
				printf ( "error : The overflow learns by experience\n" );
				exit   ( 0 );
			}
			result = lop / rop;
			mod    = lop % rop;

			//	割り算の結果として余りが出なかった場合 
			if ( mod == 0 ) {
				printf ( "%lld %c %lld = %lld", lop, op, rop, result );
				exit   ( 0 );
					
			//	割り算の結果として余りが出た場合 
			} else if ( mod != 0 ) { 
				printf ( "%lld %c %lld = %lld余り%lld", lop, op, rop, result, mod );
				exit   ( 0 );
			}
			break;
		
		default:
			printf ( "error : Please input either of operator '+', '-', '*', '/'\n" );
			exit   ( 0 );
	}

	//	加、減、乗算のオーバーフローを考慮
	if ( ( result > max ) || ( result < min ) ) {
		printf ( "error : The overflow learns by experience\n" );
		exit   ( 0 );
	}
	
	//	数値1 演算子 数値2 = 演算結果の形式で出力する  
	printf ( "%lld %c %lld = %lld", lop, op, rop, result );
	exit   ( 0 );
}
 
 

長くなってしまって申し訳ありません。

box

Re:無題

#11

投稿記事 by box » 18年前

数値と演算子とを必ず空白で区切っているのであれば、
argv[/url] から取り出せますが、今行なおうとしているのは、
スペースがなくても(そして、あっても)
正しく計算できるようにすることですよね。

ということは、コマンドライン引数を使うのではなく、
fgets() か何かで1行分を取ってきて、
中にスペースがあったら無視して、
1文字ずつ見ていきながら数値と演算子に分けて、
スタックに格納しながら計算していく、という必要があるのではないでしょうか。

管理人

Re:無題

#12

投稿記事 by 管理人 » 18年前

コマンドラインから行う方がよけいに面倒な処理になると思います。
どうしてもコマンドラインから行いたいなら複数にわかれている文字列を一度全部ひとまとまりにした方がいいかもしれません。

カッコを含むスタックを個別に用意する方法がよくわからないので、boxさんの方がずっといい回答をなさると思うのですが、
私は上記方法がダメなら、一つずつ個別に判定していく方法しか思いつきません、すみません(_ _|||)

@とんぷぅ~

Re:無題

#13

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。boxさん。レスありがとうございます。

<スペースがなくても(そして、あっても)
これはスペース無しだけの場合ならコマンドライン引数を
取得する方法で可能なのでしょうか?

<コマンドラインから行う方がよけいに面倒な処理になると思います
そうですよね。すごく大変そうです。明日もう一度確認してみますね。

<カッコを含むスタックを個別に用意する方法
私もこれを考えたのですが、どうコーディングしようか考え中です。

皆様の意見を参考にしながら、明日もう一度調べてみます。
そしてたぶんまた来ます(笑)

ちなみに、①~③まで一度に出来ればいいのでしょうけど、私には
難しそうなので、まずどの条件からクリアすればよいのかアドバイス頂ければ
幸いです(もしくは一度にやった方がよいのでしょうか?)
よろしくお願いします。

Justy

Re:無題

#14

投稿記事 by Justy » 18年前

多倍長整数とは難しそうですが
 たしかに多倍長整数を効率よく処理するルーチンをいきなり書くのは大変かもしれませんが、
多少効率を無視していいのなら比較的簡単です。

 小学校の頃に加減乗除の筆算って習いませんでしたか?
 加算なら縦に2つの数字を並べて下の桁から1桁ずつ対応した桁同士を処理し、
溢れたら溢れた分を次の桁に、ってアレです。

 アレをそのままプログラムに落とし込めば・・・40桁限定なら、10進数での桁数、
各桁の数字(40個の配列)、符号の情報を構造体にして管理すればかなり簡単に
多倍長整数を扱えるはずです。



引数については結合か結合
 どちらかといったら結合の方がいいかもしれませんね。
 ついでにスペースやタブなどの空白が含まれていたら削りながら
結合すると、必要な式の部分だけが残ってより解析しやすくなるはずです。



スペース無しだけの場合ならコマンドライン引数を取得する方法で可能なのでしょうか?
 式が入っている引数の数の分だけ文字列を結合するのが面倒であれば、
最悪、コマンドラインから与える式を ""(ダブルクォーテーション)で囲めば1つの
引数として式が main関数に入ってきますよ。


①~③まで一度に出来ればいいのでしょうけど
まずどの条件からクリアすればよいのか
 んー、私なら、

1 まず本当にコマンドラインから式を受け取る必要があるのかどうか
 必要があるなら、コマンドライン引数から文字列の結合を行い式を組み立てる。
 必要がないなら、適当にユーザーに入力させる
 
2 桁数の問題

3 最後に括弧を含む式の解析。

 ですね。

@とんぷぅ~

Re:無題

#15

投稿記事 by @とんぷぅ~ » 18年前

Justyさん。レスありがとうございました。

大変参考になりました。出来るか不安ですが
とにかくやってみます。ありがとうございました。

管理人さん。boxさん。Justyさん。
お礼を忘れていました。すみませんでした。

@とんぷぅ~

Re:無題

#16

投稿記事 by @とんぷぅ~ » 18年前

お世話になっております@とんぷぅ~です。やはりコマンドラインから
式を受け取る必要があり、スペース有りor無しの場合でも計算できるように
しないといけないみたいです^^;

box

Re:無題

#17

投稿記事 by box » 18年前

ということは、コマンドライン引数で受け取った文字列から
スペースを取り除いた形で1つの文字列にまとめ、
その文字列を1バイトずつ見ていき、
 ・'0'~'9' の場合
 ・'+', '-', '*', '/' の場合
 ・'(', ')' の場合
 ・その他の文字の場合
に分けて考えていけばよいと思います。

@とんぷぅ~

Re:無題

#18

投稿記事 by @とんぷぅ~ » 18年前

boxさんレスありがとうございます。

<スペースを取り除いた形で1つの文字列にまとめ
すみません。この部分の意味が理解できません。どう
いったことなのでしょうか?

box

Re:無題

#19

投稿記事 by box » 18年前

<<スペースを取り除いた形で1つの文字列にまとめ

失礼しました。当方が勘違いしておりました。
複数のコマンドライン引数に分かれている文字列群には
スペースを含みません。
というわけで、sprintf()あたりを使って、単純に結合すればよいです。

@とんぷぅ~

Re:無題

#20

投稿記事 by @とんぷぅ~ » 18年前

boxさん。度々ありがとうございます。

<sprintf()あたりを使って、単純に結合すればよいです。
sprintfで結合した場合、以下のような結果を求められる
ということですよね?

例.コマンドラインから受け取った引数が 1 * 2の3つの場合

引数に空白無し
ファイル名.exe 1*2 = 2

引数に空白有り
ファイル名.exe 1 * 2 = 2

box

Re:無題

#21

投稿記事 by box » 18年前

何だか話がちぐはぐになってきた気がしています。

>コマンドラインから受け取った引数が 1 * 2の3つの場合

3つ、つまりargv[1]~argv[3]に受け取ったということは、
1と*と2の間にそれぞれスペースがあった、ということです。
したがって、

>引数に空白無し
>ファイル名.exe 1*2 = 2

こちらのケースはありません。

>引数に空白無し
>ファイル名.exe 1*2 = 2

こうなるのは、1と*と2を続けて(スペースを空けずに)書いたときで、
この場合、argv[1]に"1*2"という内容を格納しています。


1と*と2の間にそれぞれスペースを空けて書いたのなら
argv[1]~argv[3]の3つを結合して最終的に"1*2"という文字列を
組み立てればよいです。
一方、1と*と2をスペースなしに書いたのなら、
見るべきところはargv[1]だけなので、最初から"1*2"という文字列を得ています。

いずれにせよ、argv[1]~argv[n]のn個(nは1以上)の文字列を結合して
数式の文字列(途中にスペースがない)を得ればよいのです。
その後、文字列の中身の解析を行ないます。

@とんぷぅ~

Re:無題

#22

投稿記事 by @とんぷぅ~ » 18年前

boxさんありがとうございました。

<1と*と2の間にそれぞれスペースを空けて書いたのなら
argv[1]~argv[3]の3つを結合して最終的に"1*2"という文字列を
組み立てればよいです。

これはコマンドライン引数をargv[1]~argv[3]まで取得したという
ことですよね?それなら結合しなくても結果は出るのではないでしょうか?
どうしても結合するということが理解できません。

<いずれにせよ、argv[1]~argv[n]のn個(nは1以上)の文字列を結合
して数式の文字列(途中にスペースがない)を得ればよいのです。

コマンドライン引数を取得して実行する方法は普通、プロンプトで
引数にスペースを空けて行いますよね? 例 1 + 1
それが引数にスペースを空けないで実行するということが
やはり引っかかってきます。 例 1+1

1 + 1の場合は引数がargv[1]~argv[3]に格納されていることは
理解できるのですが、1+1の場合は一つの文字列としてargv[1]のみ
に格納されているといった考えでよいのでしょうか?
もしその考えが合っているならば、引数のチェック(この場合だと)
if ( argc != 4 ) {
printf ( "usage : %s lop(Left operand) op(Operator) rop(Right operand)\n", argv[0] );
exit ( 0 );
}
この部分からおかしいということなのでしょうか?
混乱してきました。知識不足で申し訳ありません。
文章がおかしい部分も多々あると思いますが、よろしくお願いします。

box

Re:無題

#23

投稿記事 by box » 18年前

このスレッドの最初の投稿で、
「スペースを入れなくても計算できるようにしたい」旨
書かれていますね。
これと、

>それが引数にスペースを空けないで実行するということが
>やはり引っかかってきます。 例 1+1

これとは矛盾していませんか?
結局どうしたいのでしょうか?

@とんぷぅ~

Re:無題

#24

投稿記事 by @とんぷぅ~ » 18年前

boxさん。レスありがとうございます。

分かりにくくてすみません。結局引数で1+1 としても、1△+△1としても
四則演算出来るようにしたいです(この場合ならどちらも2になるようにしたいということです)
(△はスペースです。)

box

Re:無題

#25

投稿記事 by box » 18年前

> 結局引数で1+1 としても、1△+△1としても

ということでしたら、私の本日15:15の投稿の
最後の3行をもう一度ごらんください。

@とんぷぅ~

Re:無題

#26

投稿記事 by @とんぷぅ~ » 18年前

管理人さんタイトル変更ありがとうございました。
タイトルつけるの忘れてました^^;

boxさんレスありがとうございます。
文字列の結合、解析あたりが理解できていないのですね。
ご指摘ありがとうございました。頑張ります~。

Justy

Re:無題

#27

投稿記事 by Justy » 18年前

 @とんぷぅ~さん、この課題は多分例によって上司の方に出されたものだと思うのですが、
この問題は少し敷居が高い感じがします。

 前と同じように新人研修の課題ですか?


>どうしても結合するということが理解できません
 もう既に理解されているかもしれませんが、一応簡単に。
[color=#d0d0ff" face="monospace] > xxxx.exe 1+3 (- 4 *-2 )[/color]
 この状態で実行すると
 
 argv[1] ・・・ "1+3"
 argv[2] ・・・ "(-"
 argv[3] ・・・ "4"
 argv[4] ・・・ "*-2"
 argv[5] ・・・ ")"

 という状態で main関数に文字列が渡ってきます。

 これを解析する場合、変数が複数に分かれているので、とても扱いづらい状態です。
(表示することを考えても5つもあると面倒ですよね? ましてや実行してみないといくつに
わかれているかはわからない)

 そこで、

combine[/url] ・・・"1+3(-4*-2)"

 とこのように1つの文字列にして纏めてしまえば、解析・表示が楽になる、
というわけです。
 これが文字列の「結合」です。



[color=#d0d0d0" face="sans-serif]# ちなみに挙動としてはこんな感じですかね? (calc_test.exe)
[/color]




07/05/24 22:32 バージョンアップ

管理人

Re:無題

#28

投稿記事 by 管理人 » 18年前

バイトで遅くなりました・・((グッタリ
お力になれず、すみません、、。

あ、コマンドラインの受け取った文字列達をまとめると言うのはJustyさんの仰ったとおりです。

コマンドラインの入力値を一つの文字列にまとめるプログラムのサンプルだけお書きします。
#include <stdio.h>
#include <stdlib.h>
#define N 1000

int main( int  argc , char  *argv[ ] ){
	int i,j,s=0;
	char st[N];

	for(i=1;i<argc;i++){
		j=0;
		while(argv[j]!=NULL)
			st[s++]=argv[j++];
	}

	st='\0';
	printf("%s\n",st);

	return 0;
}


実行結果

C:\test3\Debug>test3 (1+2) * (a /b - 1)       /2.1 +1
(1+2)*(a/b-1)/2.1+1
 
 
省きましたけど途中に
 
			if(s>=N){
				printf("多すぎ");
				exit(99);
			}
 
 
とか実際は書いた方がいいと思います。

おやすみなさいませ・・zzZZ

@とんぷぅ~

Re:無題

#29

投稿記事 by @とんぷぅ~ » 18年前

Justyさん。管理人さん。レスありがとうございました。

<前と同じように新人研修の課題ですか?
はい(。´Д⊂)今回は「今までより難しいよ」としっかり
言われておりました。まだポインタや構造体等etc理解出来て
いないので、Cのサイトと格闘中です。

結合について、具体的に書いていただきありがとうございました。
大変分かりやすかったです。

<バイトで遅くなりました・・((グッタリ
お疲れ様です。いつもお世話になってます。
サンプルありがとうございました。Justyさんにいただいた物も
含めて動かしてみます。

管理人

Re:無題

#30

投稿記事 by 管理人 » 18年前

あ、途中配列要素数の所に++使っている部分が解り難かったかも知れませんので、
補足です。
以下を見てもらえたら解るとおり、i++は値が評価された後で1増やす。
++iはまず最初に1増やすと言う事です。
つまり、式の中でとiを指定してその後、i++;と2行に分けて書くのと同じ事です。

#include <stdio.h>

int main(){
	int i=0;
	char st[/url]="123456";

	printf("%c\n",st[i++]);
	printf("%c\n",st[i++]);
	printf("%c\n",st[i++]);
	printf("%c\n",st[i++]);

	return 0;
}

実行結果
1
2
3
4

#include <stdio.h>

int main(){
	int i=0;
	char st[/url]="123456";

	printf("%c\n",st[++i]);
	printf("%c\n",st[++i]);
	printf("%c\n",st[++i]);
	printf("%c\n",st[++i]);

	return 0;
}

実行結果
2
3
4
5

 
 

@とんぷぅ~

Re:無題

#31

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。レスありがとうございました。

「i++」は、iが評価されてからインクリメント
「++i」は、インクリメントされてからiが評価

この違いなのですね、基本ですがおろそかにしていました。
こういう細かい所も大変勉強なります。ありがとうございます。

Justy

Re:無題

#32

投稿記事 by Justy » 18年前

はい(。´Д⊂)今回は「今までより難しいよ」としっかり 言われておりました
 今時の新人研修はここまでやりますか・・・。
 このレベルの課題は2,3年目くらいにならないと、なかなか作れるものじゃないですよ。

 多分上司の方は「どこまで作れるか」「どう解決しようとしたのか」「何かできないのか」を
見ているのでしょうね。

@とんぷぅ~

Re:無題

#33

投稿記事 by @とんぷぅ~ » 18年前

>多分上司の方は「どこまで作れるか」「どう解決しようとしたのか」「何かできないのか」見ている
 思い通りに動くようになったら、コードを1文ずつ説明して、
 どんな考えで作ったのか報告しなければならないので、
 恐らくそういうことを見ているのだと思います。

まあ正直しんどいですが、頑張ります~。Justyさん。これからもよろしくお願いします。

@とんぷぅ~

Re:無題

#34

投稿記事 by @とんぷぅ~ » 18年前

すみません。管理人さんのコードで分からない
部分があるのですが、コメントをいただけない
でしょうか?

for(i=1;i<argc;i++){

j=0;

while(argv[j]!=NULL)

st[s++]=argv[j++];

}

上記のwhile部分が具体的にどう連結に結びつくのでしょうか?
あとst='\0';配列をヌルを入力しているのはなぜなのでしょうか?
よろしくお願いします。

管理人

Re:無題

#35

投稿記事 by 管理人 » 18年前

例えば今、
int a[10];
で宣言すると、初期化をしていないので、中身はゴミが入っていますね。

 [0]  [1]  [2]  [3]  [4]............
--------------------------..........
|  |  |  |  |  |..........
--------------------------..........

↓

--------------------------
|ゴミ|ゴミ|ゴミ|ゴミ|ゴミ|
--------------------------

a[0]='a';
a[1]='b';
a[2]='c';

と代入すれば

 [0]  [1]  [2]  [3]  [4]
--------------------------
| a  | b  | c  |ゴミ|ゴミ|
--------------------------

こんな感じになります。
コンパイラは入っているものがゴミなのか、代入した値なのかわかりませんから、
%s(終端記号までの文字列を表示せよ)で表示すると、終端記号まで探して表示しますから、ゴミまで表示されます。
ですから'c'を代入したところで文字列を終わりにしたいならちゃんと終端記号をいれてやらないと

a[3] = '\0';
 [0]  [1]  [2]  [3]  [4]
--------------------------
| a  | b  | c  | \0 |ゴミ|
--------------------------

文字列がどこまでなのかわかりません。
今回は%sで表示すると、[2]までの値が表示されるはずです。

文字列の最後には必ず終端記号が必要です。

char a[10]="abc";

と宣言してもちゃんとコンパイラがa[3]='\0';してくれているので、きちんと表示できるのです。
終端記号をいれずに表示してみてください。ゴミも表示されるはずです。
 
 

管理人

Re:無題

#36

投稿記事 by 管理人 » 18年前

サンプルです。
#include <stdio.h>

void main(){
	int i;
	char a[32];
	a[0]='a';
	a[3]='b';
	a[7]='c';
	printf("%s",a);
//	for(i=0;i<32;i++)
//		printf("%d-%d\n",i,a);
	return ;
}

 
 
こちらの環境では[0][3][7]にたまたまNULLが入っていたので、他の値に書き換えました。
表示不可能な意味不明文字になります。
コメントアウトをはずすことで、最初どんな値が入っているか確認できます。

管理人

Re:無題

#37

投稿記事 by 管理人 » 18年前

argcとargvの仕組みについては理解していますか?コマンドラインの入力文字がどのように格納されるか理解していますか?

管理人

Re:無題

#38

投稿記事 by 管理人 » 18年前

argvは2次元配列だと思って、そのそれぞれにスペースで区切った文字列が入るイメージを思い浮かべればいいです。
test.exe (3+ 1)/23-9 + 41*2

char argv[5][10];

strcpy(argv[0],"test.exe");
strcpy(argv[1],"(3+");
strcpy(argv[2],"1)/23-9");
strcpy(argv[3],"+");
strcpy(argv[4],"41*2");

 
 
実際こうではないですが、こういうイメージです。
ん~逆にややこしいですかね。

まぁとにかく

argv[1]     [2]        [3]    [4]
    "(3+" , "1)/23-9" , "+" , "41*2"

にわかれている文字列を

st
  "(3+1)/23-9+41*2"

にしたいんです。
で、

st[0] = argv[1][0];

をすると "(" が入るのはわかりますよね?argv[1][1]は "3" argv[1][2]は "+" argv[1][3]はヌルです。
これがヌルではない間コピーします。すると最初の文字列がコピーできます。
stに入れた添え字はそのままに、次に新しい文字列 "1)/23-9" を格納すると連続してコピーできます。
それを最後までコピーしていって、最後にヌル文字いれれば全部コピー完了した文字列の出来上がりです。
 
 

@とんぷぅ~

Re:無題

#39

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。レスありがとうございました。

argc、argvについて細かく調べた所、より理解が深まりました。
ただ、奥が深かったのでもう少し、ポインタ、配列、文字列の絡みを学習してきます。
特に多次元配列になるとテンパってしまって(汗)

p.s Justyさんが配布してくれたプログラムのような
動きに少しでも近づけるように頑張ります。

Justy

Re:無題

#40

投稿記事 by Justy » 18年前

思い通りに動くようになったら、コードを1文ずつ説明して、
 うわー、それはキッツイですね。
 それじゃぁ、曖昧な部分があったら速攻で突っ込まれますからね、大変だ。


Justyさんが配布してくれたプログラムのような
動きに少しでも近づけるように頑張ります。
 計算結果しか出していないんで、どれだけ参考になるかわかりませんが、
@とんぷ~さんのプログラムが完成した際の結果の検証くらいには使えるかと。


 あ、そうそう。
 ちょっとバグがあったので上のプログラムを更新してあります。 
 一見無限桁の演算ができるようにみえて、実は理論的に5万桁以上同士の乗算をしたときに
計算結果が希におかしくなる問題があったのでこっそり修正しました。

 まだ何か結果がおかしいようでしたら言って下さい。

@とんぷぅ~

Re:無題

#41

投稿記事 by @とんぷぅ~ » 18年前

Justyさんレスありがとうございました。

<ちょっとバグがあったので
そうなんですか!Justyさんのような方でも
バグがあるんでしたら私は。。。(笑)
更新ありがとうございました<(_ _)>

管理人

Re:無題

#42

投稿記事 by 管理人 » 18年前

2次元配列になったからといって特に難しい事はないですよ。
配列の先頭アドレスが[0]から順に入っていった配列が上にもう一つあるということです。
char st[4][10]={
   {"abcdef"},
   {"ghijkl"},
   {"mnopqr"},
   {"stuvwx"}
}
 
 
st[0]は"abcdef"をさしていて、
st[1]は"ghujkl"をさしています。
st[0][0]は'a'をさしていて、
st[2][1]は'n'をさしています。

理論は難しくなくてもなれていないと難しく感じるものですよね。
要は慣れですので、とにかく色んな配列のサンプルを実行したり、
自分でプログラムを書いてドンドンコンパイルする事が一番だと思います。
頑張ってください。

@とんぷぅ~

Re:無題

#43

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。レスが遅れて申し訳ありませんでした。

質問があるのですが、例えば1+1として(スペース無し)の場合
argv[1]に1+1が入っているので、その文字列を解析して、数値なら
'1'と'+'と'1'という具合に解析をして計算させれば良いのでしょうか?

解析が出来ず、困っています。isdigit関数などを使用して数値かの
判別をすれば良いのでしょうか?質問ばかりで申し訳ありませんが
よろしくお願いします。

管理人

Re:無題

#44

投稿記事 by 管理人 » 18年前

1文字ずつ調査すればいいのではないでしょうか?
例えば
argv[1][0]には今「1」が入っていますよね?
argv[1][0+1]には「+」が入っています。
この数字と文字を分けたいということなんですよね?

現在調査している要素が文字なら、そこで調査を一時停止して、そこまでを一つの文字列とし、
atoiなどを用いて文字を数値に変換します。

調査を開始して、次は文字のはずですから、1文字とってきて演算子として記憶する、次はカッコか数値のはずですからそれ以外ならエラー、などではどうでしょうか?

つまり、今

char st[/url]="123+456*-";

こんな文字列が入っているとします。

st[0]を調査、数値なので、次へ
st[1]を調査、数値なので、次へ
st[2]を調査、数値なので、次へ
st[3]を調査、記号なので[2]までを一つの文字列として、その文字列を数値に変換。どこかへ保存。
st[3]は演算子としてどこかに保存。
(st[4]はカッコか数値のはず。)
st[4]を調査、数値なので、次へ
st[5]を調査、数値なので、次へ
st[6]を調査、数値なので、次へ
st[7]を調査、記号なので、[6]までを一つの文字列として、その文字列を数値に変換。どこかへ保存。
st[7]は演算子としてどこかに保存。
(st[8]はカッコか数値のはず。)
st[8]を調査、カッコでも数値でもないのでエラー終了。

こんな感じでどうでしょう?

Justy

Re:無題

#45

投稿記事 by Justy » 18年前

# @とんぷぅ~さんへ
例えば1+1として(スペース無し)の場合
argv[1]に1+1が入っているので、その文字列を解析して
 argv[2]以降に一切「式」が入っていないことが保証できるのであれば、argv[1]を解析してください。


数値なら '1'と'+'と'1'という具合に解析をして計算させれば良いのでしょうか?
 基本的にはそうです。
 ただ、括弧付きを扱うのであれば計算の順番を正しく判定する必要があります。



isdigit関数などを使用して数値か判別をすれば良いのでしょうか?
 数値かどうかなら yes。



# 管理人さんへ
?st[8]を調査、カッコでも数値でもないのでエラー終了。
 式が "123+456*-"ならエラーになりますが、"123+456*-5"ならエラーにはならないですよね?

管理人

Re:無題

#46

投稿記事 by 管理人 » 18年前

Justyさん、
-5をかけるとき、
a*(-5)とかかないと数学的な式としておかしくないでしょうか?
私は数式でマイナスを書けるとき必ず括弧を書きますけど、省略できるんです?
しかし数学的な式として入力するなら
2a
という入力もありにしないといけなく・・。
どこまで実現するべきかは仕様によりますね。
もし*-という連続を許すならそこのエラー処理は必要ないと思います。

@とんぷぅ~

Re:無題

#47

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。Justyさん。お世話になっております。

>argv[1][0+1]には「+」が入っています
この[0+1]というのはどういうことなのでしょうか?

>この数字と文字を分けたいということなんですよね?
はい。数字であるならatoiなどで、数値に変換して計算
可能な状態にするようにしたいです。

>argv[2]以降に一切「式」が入っていないことが保証できるのであれば、argv[1]を解析してください。
今回は引数が最大でも4つになる( 1 + 1 = 2 )ので、スペースを入れない場合argv[1]のみになります。
(考え方が間違っていたらすみません)

>括弧付きを扱うのであれば計算の順番を正しく判定する必要があります。
今のところまず、括弧や桁数を意識しないで、スペース有りor無しで普通に
四則演算が出来るコードを書いてみようと思います。

ちなみに2007/05/22(火) 22:07でのコードを元に作成しているのですが、
コードを根本的に変えていかなければならないのでしょうか?

ちなみに解析で文字列を読み込む際に、コマンドライン引数から読み込む
ことは可能なのでしょうか?getcharなどを読み込みに使うと入力待ちに
なってしまう為、よろしくないです。

長々となってしまい申し訳ありません。よろしくお願いいたします。

管理人

Re:無題

#48

投稿記事 by 管理人 » 18年前

キーボードからの入力を必要としないならgetcharは必要ないでしょう。
もうすでに配列に文字列がはいっているのですから、配列要素を一つずつ調べたら同じ事です。
コマンドラインから可能かという質問は前にもお答えしましたよね?
複数にわかれた文字列を一つにするプログラムのサンプルもお書きしたと思います。
括弧なしの計算なら簡単です。
私の書いたスタックに合うように文字列をおいてやればいいだけです。
しかし括弧つきに応用が出来ませんから、もし括弧を実現すべきなのなら1からほかの考え方で作ったほうがよさそうです。
すみませんが、私には括弧つき計算でいい方法が思いつきません。

Justy

Re:無題

#49

投稿記事 by Justy » 18年前

# 管理人さん
私は数式でマイナスを書けるとき必ず括弧を書きますけど、省略できるんです
 一応省略しても「式」的には正しく読みとれそうですが。

 たしかに言われてみれば "3*(-5)"では省略はしない方がいいのかもしれませんね。
 でも "-5*3"の時は省略しますね。
 う~む。この場合も括弧つけた方がいいでしょうか。"(-5)*3"みたいに

 あとは()の前に付く場合ですね。 "4 * -(1 + 5)"とか。


>どこまで実現するべきかは仕様によりますね
 ですね。



# @とんぷぅ~さん
括弧や桁数を意識しないで、スペース有りor無しで普通に
四則演算が出来るコードを書いてみようと思います
 つまり括弧はなし、桁数はlong longの範囲で、スペースの有無はどちらでも、ということですね。


コードを根本的に変えていかなければならないのでしょうか
 最終的なコードは大幅に異なるものになりそうです。
 が、上記の条件(括弧なしとか)であれば延長線上でいけそうな気がします。


コマンドライン引数から読み込む ことは可能なのでしょうか?
 今までどこから読み込んでいたのでしょうか?

@とんぷぅ~

Re:無題

#50

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。Justyさん。レスありがとうございます。

>もうすでに配列に文字列がはいっているのですから、配列要素を一つずつ調べたら
はい。その通りなのですが、その「調べる」ということが理解出来ておりません。
何が分かっていないからなのでしょうか?


>私の書いたスタックに合うように文字列をおいてやればいいだけ
スタックについてのサンプルありがとうございました。しかし今回
どうしても、自分でコーディングしたソースを元に作りたいのです。
(スペース有りor無しだけについて)


>コマンドライン引数から読み込む ことは可能なのでしょうか?
すみません。コピペでミスをしてしまいました。意味不明なことを
言ってすみませんでした。


>つまり括弧はなし、桁数はlong longの範囲で、スペースの有無はどちらでも、ということですね。
はい。そういうことになります。


>どこまで実現するべきかは仕様によりますね
明記するのを忘れておりました。すみません。
今回は 1 + 2 - 3などの3項演算?はしないので、括弧による優先順位は関係ありません。
括弧をつける基準としては入力された数値が負数の場合は括弧で括るということだけで大丈夫です。


>数値かどうかなら yes
isdigitを使ってargv[1]に入っている文字列を解析しようと
思ったのですが、中々上手くいきません。。


こんなに有用な意見を頂いているのにやりたいことがパッと出てこない為、
皆様に大変迷惑を掛けてしまってすみません。

管理人

Re:無題

#51

投稿記事 by 管理人 » 18年前

すみません、ちょっと忙しくてお返事がちまちまになりそうです。

>しかし今回どうしても、自分でコーディングしたソースを元に作りたいのです。

人のプログラムを理解するのは大変ですし、自分で書いたほうが力になるので出来ることならそちらがいいと思います。
アルゴリズムはどのように考えています?
スタック使うのでしょうか?

> 1 + 2 - 3などの3項演算?はしないので

という意味がよくわからなかったのですが、今単純に「1+2」とか「3*2」とか3つ以上の数値は入力しないということなのでしょうか?(そんな簡単な事じゃないですよね^^;

>こんなに有用な意見を頂いているのにやりたいことがパッと出てこない為、皆様に大変迷惑を掛けてしまってすみません。

具体的なアドバイスが出来ていないので、わからなくても仕方ないと思いますし、遠慮なく情報交換しましょう。
結構最初の仕様を実現するのは難しいように思います。

Justy

Re:無題

#52

投稿記事 by Justy » 18年前

>括弧をつける基準としては入力された数値が負数の場合は括弧で括るということだけで大丈夫です。
 なるほど、そこまでシンプルになれば相当簡単になりますね。
 スタックも不要ですね。

 2007/05/22(火) 22:07のソースから改良する、ということでしたが
この規模まで小さくなったら1から書き直した方が早そうな気もします。

 スペース有りor無しでも動くように、文字列の結合から着手してみてはどうですか?


>isdigitを使ってargv[1]に入っている文字列を解析しようと思ったのですが、中々上手くいきません
 どううまくいかないのでしょうか。

@とんぷぅ~

Re:無題

#53

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。Justyさん。レスありがとうございます。

"管理人さん"

>今単純に「1+2」とか「3*2」とか3つ以上の数値は入力しないということなのでしょうか?
はいそうです。こんなことで質問して申し訳ありません。
ちょっと課題が難しいのでまずここからやってみてっていう感じになりました。

>結構最初の仕様を実現するのは難しいように思います。
そうですね~。最終的には最初の仕様になると思います。

"Justyさん"

>スペース有りor無しでも動くように、文字列の結合から着手してみてはどうですか?
こちらは管理人さんから頂いたサンプルを使用しているのですが、単純に
sprintf ( str, "%s", argv[1] )等でも良いのでしょうか?はたまたstrcat等を
使用するのか?その辺が疑問です。

>isdigitとargvでは型が違うので、上手くいかずに困っています。argvに入っている
文字列を1文字ずつ確認するには違う関数を使うのでしょうか?もしくはargvに入っている
文字列を切り出してから、1文字ずつ解析を行うのでしょうか?トークンあたりを調べて
みたのですが、あまり理解出来ませんでした。

長々失礼しました。

管理人

Re:無題

#54

投稿記事 by 管理人 » 18年前

とんぷ~さんの回答を拝見するに、ちょっとトークンなどのライブラリ関数を用いずに、
文字列と配列、アドレスや文字コードなどの勉強もかねて、配列1つ1つを操作するプログラムを書いた方がいいように思います。
ライブラリ関数を用いるより自分で配列の内容を確認しながら作っていった方が仕組みがよくわかりますしね。

ちょっと戻って1から基本を見つめなおしましょう。
文字列というものは配列要素[0]から'\0'が入っている配列要素までをひとまとまりとするとかその辺の事をしっかりおさらいしてください。
きっと基本があやふやだと応用を効かせようとしたとき無理が来てわからなくなってしまうと思いますので。

では
「1+1」
が文字列として存在するとき
「1」「+」「1」にわけて2次元配列に格納する事から始めましょう。
単純に四則演算だけならすごく簡単ですから。

管理人

Re:無題

#55

投稿記事 by 管理人 » 18年前

サンプルを書きました。
入力はスペース無しで2つの値の四則計算のみという条件です。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void main(void){

	int i=0,x[3];
	char st[/url]="1+1",c;

	while(st!='\0'){
		if( !(st>='0' && st<='9') ){
			c=st;
			st='\0';
			x[0]=atoi(st);
			x[1]=atoi(&st[i+1]);
			break;
		}
		i++;
	}

	printf("%d %c %d = ",x[0],c,x[1]);

	switch(c){
		case '+':	x[2]=x[0]+x[1];	break;
		case '-':	x[2]=x[0]-x[1];	break;
		case '*':	x[2]=x[0]*x[1];	break;
		case '/':	x[2]=x[0]/x[1];	break;
	}
	printf("%d",x[2]);
}

実行結果

1 + 1 = 2

 
 

管理人

Re:無題

#56

投稿記事 by 管理人 » 18年前

 
st
|-[0]---[1]---[2]---[3]--|
|  1  |  +  |  1  |  \0  |
|------------------------|

stの中身は上のような配置になっています。

while(st!='\0'){

で終端記号を見つけるまで順に配列の中身を調査していきます。
まず[0]です。

if( !(st>='0' && st<='9') ){

という意味は今調査している配列要素の中身が文字コード「0」から「9」までの間ではなかったら
と言う意味です。数字か数字じゃないかという事ですね。
今回はスペースなし、記号は四則演算子しか入らないという条件の元これで可能です。

今[0]は「1」ですから条件にマッチせず、i++により[1]に調査対象が移動します。

[1]は「+」ですからif文の条件にマッチします。

c=st;

これでcに演算子を格納します。

st='\0';

これにより、演算子までを一つの文字列として区切ります。
ただ今以下のようになっています。

st
|-[0]---[1]---[2]---[3]--|
|  1  |  \0 |  1  |  \0  |
|------------------------|

stと書くと[0]のアドレスの事です。&st[0]は配列の先頭アドレスで、文字列は[0]から始まり[1]で終わる文字列です。
一方文字列というのは示したアドレスから進んで終端記号が見つかるまでのひとまとまりですから、
[2]のアドレスを示せば、[2]から[3]までの文字列が示せる事になります。
ですから

x[0]=atoi(st);
x[1]=atoi(&st[i+1]);

で、それぞれ2つの値が格納できる事がわかりますか?stと書いてありますが、
x[0]=atoi(&st[0]);
と
x[0]=atoi(st);
は同じ意味です。

break;

で処理を抜けます。
後は条件によって計算し、表示しているだけです。
おわかりになるでしょうか。

変にライブラリ関数を使うより配列の扱い方を勉強しながら順々に作ってみてはいかがでしょうか。
仕組みを理解しながら作っていった方が応用が効く様に思います。

また、サンプルの文字列はいろいろ変更してみて確認してください。

例)
char st[/url]="4358-358";

実行結果

4358 - 358 = 4000

 
 

Justy

Re:無題

#57

投稿記事 by Justy » 18年前

単純にsprintf ( str, "%s", argv[1] )等でも良いのでしょうか?
はたまたstrcat等を 使用するのか?その辺が疑問です。
 後者の方が判りやすいとは思いますが、どちらでもOKです。


isdigitとargvでは型が違うので、上手くいかずに困っています。
argvに入っている文字列を1文字ずつ確認するには違う関数を使うのでしょうか?
 その関数で合ってますよ。
 
[color=#d0d0ff" face="monospace]#include	<stdio.h>
#include	<string.h>
#include	<ctype.h>

int main(int argc, char *argv[/url])
{
	if(argc >= 2)
	{
		size_t n, length;
		const char *p = argv[1];
		length = strlen(p);
		for(n=0; n<length; ++n)
		{
			char c = p[n];
			printf("%d文字目は [%c] ... ", n+1, c);
			if(isdigit(c))
				printf("%s\n", "数字");
			else
				printf("%s\n", "数字以外");
			
		}
	}
	return 0;
}[/color]
 argv[1]の中を1文字1文字解析して、数字かそれ以外かを判定しています。
 一応 isdigit(c)という形にまとめましたが、cは p[n]なので、isdigit(p[n])としても
isdigit(argv[1][n])としても正しく動くはずです。

@とんぷぅ~

Re:無題

#58

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。Justyさん。レスありがとうございました。
ネットにトラブルがあり、レスが大変遅れて申し訳ありません。

"管理人さん"

>ちょっと戻って1から基本を見つめなおしましょう。

そうですね。今まで行き当たりバッタリで課題を行っていたので、
苦手と思う部分や、面倒と思う所を省いていました。今ままで配列にアクセス
するコードを書いた機会があまり無く、配列とループのセットが苦手なことが分かりました。
得に添え字が変数に置き換えられた多次元配列とループなどが、ソースを見てもパッと理解
することが出来ません。

ちなみに配列名は配列の先頭要素のアドレスを指すという部分なのですが
st[0]なら0のアドレスを指すということは分かります。ではargv[1][0]ならば
argv[1][0]のアドレスを指すのでしょうか?それともargv[0][0]?
すみません。テンパってきました。ただ管理人さんの説明のおかげで上記サンプル
はなんとか理解できました。


"Justyさん"

>const char *p = argv[1];

この1行はどういった意味なのでしょうか?
それと、よく見かけるsize_t型についてなんですが、一体
どのような場合に使われるんでしょうか?


ちなみに今現在の仕様なら、文字列を連結せずに
出来るような気がするのですが無理ですか?
(スペース有りなら今までのソースを元にして、無しなら
argv[1]に入っている文字列を文字に分けて解析して数値ならば)
という具合です。おかしい部分があったら言って下さい。

管理人

Re:無題

#59

投稿記事 by 管理人 » 18年前

>st[0]なら0のアドレスを指すということは分かります。

いえ、違います。st[0]のアドレスは&st[0]で示せます。st[0]と書くと[0]自身のデータを示します。
char st[10];
などで宣言した配列を「st」と書くと&st[0]の意味になります。すなわちst配列の先頭アドレスです。
以下を見てください。
st[4]

st          [0]       [1]      [2]      [3]


st[3][4];

st[0]->     [0][0]    [0][1]   [0][2]   [0][3]
st[1]->     [1][0]    [1][1]   [1][2]   [1][3]
st[2]->     [2][0]    [2][1]   [2][2]   [2][3]
 
 
上の1次元配列において、stと示すと[0]のアドレスを指します。
下の2次元配列において、

st[0]とさすと、st[0][0]~st[0][3]までの1次元配列と考えられる配列の先頭アドレスすなわち&st[0][0]を指します。

st[1]とさすと、st[1][0]~st[1][3]までの1次元配列と考えられる配列の先頭アドレスすなわち&st[1][0]を指します。

以下のように宣言すると、以下のように確保されます。
char st[4]="123";

|-[0]---[1]---[2]---[3]--|
|  1  |  2  |  3  |  \0  |
|------------------------|

printf("%s",st);
と書くと「123」が表示されるのは、stが&st[0]をさしているからです。
つまりstの部分はそこから右をまとまりとしたいアドレスを書けばいいわけですから、
printf("%s",&st[2]);
こう書いてもいいわけです。
こうすると「3」が表示されます。

では応用で、2次元配列で考えて見ます。
2次元配列であろうと、3次元であろうと、配列は連続したアドレスに用意されます。
[0][0]の次のアドレスは[0][1]です。

char st[3][4];において

[0][3]の次のアドレスは[1][0]です。今

	st[0][0]='a';
	st[0][1]='b';
	st[0][2]='c';
	st[0][3]='d';
	st[1][0]='e';
	st[1][1]='\0';

で格納すると以下のように入ります。

|-[0][0]---[0][1]---[0][2]---[0][3]--|
|    a   |    b   |    c   |    d    |
|-[1][0]---[1][1]---[1][2]---[1][3]--|
|    e   |   \0   |    -   |    -    |
|-[2][0]---[2][1]---[2][2]---[2][3]--|
|    -   |    -   |    -   |    -    |
|------------------------------------|
 
printfで示すアドレス先は特にどこでもいいのですから

	printf("%s",&st[0][1]);

このように書けば「bcde」と表示されるはずです。
また、st[1]は[1]行目の1次元配列とみなせる配列の先頭アドレスですから、

	printf("%s",st[1]);

こうかけば「e」が出力されます。


#include <stdio.h>

void main(){
	char st[3][4];
	st[0][0]='a';
	st[0][1]='b';
	st[0][2]='c';
	st[0][3]='d';
	st[1][0]='e';
	st[1][1]='\0';

	printf(" st[0]    => %s\n",st[0]);
	printf("&st[0][1] => %s\n",&st[0][1]);
	printf(" st[1]    => %s\n",st[1]);
}

実行結果

 st[0]    => abcde
&st[0][1] => bcde
 st[1]    => e
 
 
 
よくわからなければなんでもいいからとにかく疑問に思う表示の仕方やデータの入れ方をなんでもいいからつっこんでガンガンコンパイルしてみてください。
コンパイルした数だけ理解が深まるでしょう。

@とんぷぅ~

Re:無題

#60

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。レスありがとうございます。

なんかこう配列名は先頭要素のアドレスという言葉で
分かった気になってしまっていました。大変分かり易い
説明ありがとうございました。もっと多次元配列のコンパイル
をして理解を深めてみます。

管理人

Re:無題

#61

投稿記事 by 管理人 » 18年前

あ、あと型についてはこちらをご覧ください。
http://www.tamasoft.co.jp/lc/hlp/F029.html

Justy

Re:無題

#62

投稿記事 by Justy » 18年前

>const char *p = argv[1];
この1行はどういった意味なのでしょうか?

 argvの宣言は char *argv[/url]ですよね?
 これは char **argvと扱い的には同じになります。
 つまりポインタのポインタです。

 で、argv[1]としたとき、これ自体の型は char *となり、普通の charポインタとなります。
 でこのまま argv[1]として使い続けてもいいのですが、一端 argv[1]のポインタ値を
別の変数に入れた方が扱いやすいので、pという変数を用意して代入しています。

 どう扱いやすくなるかというと 例えば argv[1]じゃなくて、argv[2]の解析を
しようとか考えたとき、ソースのあっちこっちに argv[1]が散乱していたら
書き換えるのが面倒ですよね?

 そんなとき pに代入してあれば代入式の argv[1]を argv[2]に代えるだけで済みます。


 又、別の変数に入れる、ということはその代入元の方の変数に別の名前を付けるという意味も生まれます。
 サンプルでは pとしたので意味もへったくれもないですが、例えば
[color=#d0d0ff" face="monospace]    const char *app_name = argv[0]
    const char *exression_string = argv[1];
[/color]
 としていたら、引数の名前の意味が
argv[0]・・・「1つ目の引数」という意味から app_name・・・「アプリケーション名」に
argv[1]・・・「2つ目の引数」という意味から exression_string・・・「式の文字列」になり

 見ただけで意味が分かりやすくなるという効果があります。


それと、よく見かけるsize_t型についてなんですが、
一体どのような場合に使われるんでしょうか?
 size_tはよく要素の数を表すのに使われます。

 例えば文字列の長さを調べる標準関数 strlen()ですが、戻り値に文字列の長さが返って来ます。
 しかし、戻り値の型は intではありません。size_tなのです。
 実際 size_tが何なのかは環境によって違いますが、大抵は unsigned intか unsigned longのようです。

 特に何もないのに積極的に size_tを使うことはありませんが、標準関数で size_t型が
指定されている場合はsize_t型を使った方がいいでしょう。

# sizeof()とかの結果の値も size_t型です。


ちなみに今現在の仕様なら、文字列を連結せずに 出来るような
気がするのですが無理ですか?
 スペースがあった場合となかった場合で、ソース(解析・計算部分)を分けると言うことですか?
 無理ではないと思いますが・・・。

 スペースはあってもなくても受け付けるということは
[color=#d0d0ff" face="monospace]calc.exe 1 + 3
calc.exe 1 +3
calc.exe 1+3
calc.exe " 1  + 3"[/color]
  このどれもを受け付けなければなりません。
 もちろん、理論的には連結しなくてもできますが、ちょっとだけ面倒になります。

@とんぷぅ~

Re:無題

#63

投稿記事 by @とんぷぅ~ » 18年前

早速のレスありがとうございます。typedefを使って定義されている型
とあったので、構造体が絡むんでしょうか?スミマセン。知識不足で
よく分かりませんでした。調べてみます。

Justy

Re:無題

#64

投稿記事 by Justy » 18年前

typedefを使って定義されている型とあったので、構造体が絡むんでしょうか
 とりあえず、size_tは要素の数を表現する型で、整数であるとだけ覚えておけば大丈夫です。

@とんぷぅ~

Re:無題

#65

投稿記事 by @とんぷぅ~ » 18年前

Justyさん。レスありがとうございます。

>これは char **argvと扱い的には同じになります。
>つまりポインタのポインタです。

ポインタのポインタ。。。言葉を聞くと一瞬泣きそうになります(笑)

>もちろん、理論的には連結しなくてもできますが、ちょっとだけ面倒になります。

ですよね。回答ありがとうございました。

>例えばargv[1]じゃなくて、argv[2]の解析をしようとか考えたとき、
>ソースのあっちこっちに argv[1]が散乱していたら
>書き換えるのが面倒ですよね?

確かにあっちこっちに散乱してる場合は面倒ですね。

ちなみに、流れ的にはargv[1]~argv[3]までを連結し、その上で
argv[/url]に入っている文字列を1文字ずつ解析し、数値なら式に沿って演算させる。
というふうな感じにすればいいんですよね?(こちらが質問者なのにすみません)

管理人

Re:無題

#66

投稿記事 by 管理人 » 18年前

>ポインタのポインタ。。。言葉を聞くと一瞬泣きそうになります(笑)

苦手意識をお持ちだという事が伝わってきますが、全然難しいものではないですよ。

例えば、ポインタとは目印だと思ってください。
データには目印がついていて、その目印をみればデータが取り出せると。

char **st;
で受け取ればポインタのポインタという事になりますが、
言い換えれば
目印が入った連続した箱の集まり、その集まりにさらに目印をつけたものの箱の集まり、ということです。

ポインタというのは言ってみれば配列です。

char a[/url]="1234";
とかいても
char *a="1234";
と書いてもいいことご存知でしょか。

上記両方ともaとかけばaのアドレスの事を示します。
2重ポインタということは、これらの配列の先頭アドレスを一つずつ格納した配列の集まりと言う事だと言い換えられます。
2重ポインタと2次元配列は同じではなく、意味は違うのですが、同じ意味も示す事が出来ます。

う~ん、、言ってて余計にわからなくさせてしまったかな・・・。

@とんぷぅ~

Re:無題

#67

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。レスありがとうございます。

配列とポインタは似て非なるものと見ました。ポインタと
配列の決定的に違う部分というのは、本を見て分かったのですが
本当に理解したとは言えません(泣)

しかも本によっての解説が違うので今いちでした。

Justy

Re:無題

#68

投稿記事 by Justy » 18年前

流れ的にはargv[1]~argv[3]までをを連結
 argcの値によって変わりそうですが、基本的にその通りです。


argv[/url]に入っている文字列を1文字ずつ解析し/i>
 argv[/url]に、というか連結して作った文字列に対して、ですね。


数値なら式に沿って演算させる
 yes.

@とんぷぅ~

Re:無題

#69

投稿記事 by @とんぷぅ~ » 18年前

Justyさんレスありがとうございます。

>argcの値によってargv[1]~argv[3]までをを連結
今回は2項演算までなので最大でもargv[3]までが引数に
なので、argcは最大で4になると思うのですが、考え方
は正しいでしょうか?

>argv[/url]に、というか連結して作った文字列に対してですね。
その通りですね。連結した文字列に対して1文字ずつ解析ですね。

しかし、自分のやりたいことをコードにするのは難しいですね。
この頃コンパイラに怒られてばかりです(笑)

Justy

Re:無題

#70

投稿記事 by Justy » 18年前

argcは最大で4になると思うのですが、考え方は正しいでしょうか?
 正しいです。


自分のやりたいことをコードにするのは難しいですね
 何かこう、プログラムの本質みたいなのが見えてくると、そのあたりはぐっと簡単になりますよ。
 その壁を越えるまでが大変ですけど・・・。

@とんぷぅ~

Re:無題

#71

投稿記事 by @とんぷぅ~ » 18年前

Justyさん。レスありがとうございます。

>何かこう、プログラムの本質みたいなのが見えてくると、そのあたりはぐっと簡単になりますよ。
>その壁を越えるまでが大変ですけど・・・。

確かにそうですね。いつになれば自分で「コーディングした」っていう
感覚で仕事が出来るのか。。。。壁は高いです。。。。(( T_T)トボトボ

管理人

Re:無題

#72

投稿記事 by 管理人 » 18年前

ちょっと意欲低下してらっしゃるようなので気分転換のコメントでも^^;


@とんぷぅ~さんはきっと現在「やらされているプログラミング」であったり、「しなければならないからやっているプログラミング」であるのではないでしょうか。そのために

なかなか思い通りに行かない・・(_ _|||)ズ~ン

見たいな心境になってしまっているのではと思いますけど、プログラムを趣味で楽しいと思ってやっているとプログラムの見方が全然違ってきますよ。
昔ポインタが嫌いでした、構造体が嫌いでした、文字列処理が嫌いでした、自作関数がわかりませんでした。
しかしゲームをちょびちょび作っていく中でそこを嫌っていたらそれ以上の処理が出来ない事に気が付きました。
もっとよいゲームを作りたい、もっとコードを賢く書きたい、もっと効率よいコードが書いてみたい。
そんな思いで色々な単元を勉強していきました。
ちょっと勉強してはゲームを実装し、ちょっと勉強してはゲームを実装しました。
私は参考書をひたすら読んで勉強した覚えなど一度もありません。(だから基礎がなってないんだといわれたらそれまでデスガ^^;)
必要な時に調べては、コーディングするという繰り返しでした。
(そうするといつも知ってる知識内でしかプログラムコードをかかなくなってしまっていたので、意図的に他のアルゴリズムを勉強したりしたことはありましたが)

私はゲームをプログラムで作る事が楽しかったので、難しいな嫌だなと思う単元も、苦に思うことなく勉強できました。
というか趣味だったので、勉強という感じがありませんでした。
それに、きっと読むばっかりの勉強ではなく、それを勉強したらすぐにそれを使ってコーディングしたのが良かったのだと思います。
数学の教科書を1冊最初から最後まで読み通してわかった気になって、いざ教科書をひらかずに練習問題やろうとしたらさっぱりできなかったというのと同じように、
プログラムも演習によって身に付いていくものです。
また、「おもしろい!」と思えばそれに勝るものはありません。

プログラムのおもしろさを先に見つけ、プログラムの面白さを実感しながら、新しい単元を演習によって身につける道が最短コースだと思います。

((といってもシゴトとかで必要なのでしたらのんびりゲームなんて作ってる暇ないですかね^^;
もしどうしてもいやになったら、今勉強している範囲を利用して小さなゲームを作ってみたりアプリを作ってみたりして自分で学習意欲を出させる事も大事かと思います。
もし時間が出来たら色んな方向からプログラムをみつめてみてください☆

・・と偉そうな事を言ってみてもまだ私はプログラムのプの字もわかってないわけですが^^;

@とんぷぅ~

Re:無題

#73

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。レスありがとうございます。

<プログラムのおもしろさを先に見つけ、プログラムの面白さを実感しながら、
<新しい単元を演習によって身につける道が最短コースだと思います。

ですね。今は楽しむではなく、やらされている感が先行してしまいますね。
もっとこう自分で楽しく作れるようになるといいんですがw

<・・と偉そうな事を言ってみてもまだ私はプログラムのプの字もわかってない

いえいえそんなことはありません。管理人さんの説明は分かりやすいですし
私からみたら雲の上の存在です。まぁ私からすればここのサイト
にいる方が皆さんそうでしw

TT414

Re:無題

#74

投稿記事 by TT414 » 18年前

>どんな整数を入力しても計算結果が正しく表示されるようにする。

不可能です。無限大のメモリまたはハードディスクがなければできません。
必ず、制限があります。

問題を出題した人に正解を聞いてみましょう(どんな答えを出すか楽しみ)。

@とんぷぅ~

Re:無題

#75

投稿記事 by @とんぷぅ~ » 18年前

TT414さん初めまして。

>どんな整数を入力しても計算結果が正しく表示されるようにする。

言い方が悪かったです。"どんな"というのは今まで扱ってきた値と比べて
かなり大きい値を扱うということで、今回ならば最終的に40桁まで扱える
ようにしたいがための表現が"どんな"という言葉で過大になってしまいました。

続けて質問失礼します。引数にスペース無しの場合でのコードを書いて
いたのですが、計算結果がおかしくなります。例・引数を1+1と入力すると
49 + 49 = 98などという結果が出ます。何処を直せばよいでしょうか?
#pragma warning ( disable : 4996 )

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

//	パラメータエラー
void usage ( void )
{
	printf ( "<< 引数を正しく入力して下さい。\n" );
	printf ( "<< 1+1 or 1 + 1 の形式で入力して下さい。\n" );
	exit   ( 0 );
}

int main ( int argc, char *argv[/url] )
{
	int  result;
	int  i = 0;
	char op;

	//	パラメータのチェック
	if ( argc != 2 && argc != 4 ) {
		usage ();
	}

	//	コマンドライン引数にスペースを与えない場合
	if ( argc == 2 ) {
		while ( argv[1] != '\0' ) {
			if ( argv[1] == '+' ) {
				op = ( argv[1][1] );
				if ( isdigit ( argv[1] ) ) {
					if ( argv[1] >= '0' && argv[1] <= '9' ) {
					}
				}
			}
			i++;
		}
	}
	switch ( op ) {
		case '+':
			result = argv[1][0] + argv[1][2];
			break;
	}

	printf ( "%d %c %d = %d\n", argv[1][0], op, argv[1][2], result );
}

 
 

恐らくコードが意味不明な部分があると思いますが、よろしくお願いします。

管理人

Re:無題

#76

投稿記事 by 管理人 » 18年前

それは「文字コード」+「文字コード」をしているからです。
受け取ったものは「文字」であり、「数値」ではありません。
「1」という文字は文字コード1ではなく、「1」の文字コードはアスキーコードによると49です。
過去に文字コードについて何度か解説しています。よければちょっと探してみてください。

数値をシングルコーテーションで囲むとそれは文字コードを意味します。
例えば
1
とかけば数値1のことですが、
'1'と書けば「1」の文字コードを意味します。

printf("%d",1);
printf("%d",'1');

両者で違う数値が出ることを確認してください。
atoiで文字を数値に変換しなければならないのはこのためです。
私の書いたサンプルの中で「1+1」の各「1」をatoiで数値に変換していますよね?
また、文字の比較をするとき「'0'以上'9'以下」という書き方をコードの中でしています。
「0以上9以下」ではないのです。

ちょっと解説が手短ですが、文字と、数値、数値を表す文字コードについて調べてみてください。

数値と文字の数字は意味が違うということです。
http://e-words.jp/p/r-ascii.html
文字コードについてはこちらに書かれている表に対応します。

また、1文字の場合、わざわざatoiしなくてもいいです。
例えばいま「9」の文字を数値の「9」として取り出したいときの話です。
「0123456789」と文字コードが並んでいるので、
「9の文字コード」-「0の文字コード」
をすれば「数値の9」が取り出せます。
例えば、
0の文字コードが49だとします。9の文字コードが58ですね。
58-49=9
つまり、引き算すれば数値が取り出せます。
二桁以上になると微妙に計算がややこしくなるのですが1文字の場合単にひけばいいですよ。

@とんぷぅ~

Re:無題

#77

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。レスありがとうございます。
atoiで数値に変換したいのですがargv[1][0]等とすると
コンパイルできません。文字を解析して格納までが上手くいきません。

後、ソースでこうした方がいいということはどんどん指摘して
下さい。よろしくお願いします。

管理人

Re:無題

#78

投稿記事 by 管理人 » 18年前

関数が利用できない

自分の関数の使い方が間違っているのかな?

それなら関数の仕様を調べてみよう


こういう流れで利用できない関数は利用できるようにしていきましょう。
まず、atoiの関数の仕様を調べてみることです。
渡すべき変数の形と現在渡している変数の形がちゃんとあっていますか?

>文字を解析して格納までが上手くいきません。

文字列を解析して数値に変換すべきものは数値に変換し、中の演算子は別途格納するサンプルは既に示しました。
サンプルをじっくりみてください。

@とんぷぅ~

Re:無題

#79

投稿記事 by @とんぷぅ~ » 18年前

>渡すべき変数の形と現在渡している変数の形がちゃんとあっていますか?

const char *stringと書いてあり型が違うので出来ないと思います。ただ
argv[1]は文字列に対するポインターの配列なのに
argv[1][n]はどうして同じではないのでしょうか?

これが分からないのは配列が分かっていなんですかね?

>文字列を解析して数値に変換すべきものは数値に変換し、
>中の演算子は別途格納するサンプルは既に示しました。

はい。参考にさせていただいて、演算子は変数char opに格納するようにしました。
ただ、演算子は一つなのでいいのですが、数字を見つけたときに、それが上書き
されてしまうんじゃないのか心配です。じゃあ格納場所を多くすればいいのか?
とも考えたんですが、実際どう組み込めばいいのか分かりませんでした。
#pragma warning ( disable : 4996 )

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

//	パラメータエラー
void usage ( void )
{
	printf ( "<< 引数を正しく入力して下さい。\n" );
	printf ( "<< 1+1 or 1 + 1 の形式で入力して下さい。\n" );
	exit   ( 0 );
}

int main ( int argc, char *argv[/url] )
{
	int  result;
	int  i = 0;
	char op;

	//	パラメータのチェック
	if ( argc != 2 && argc != 4 ) {
		usage ();
	}

	//	コマンドライン引数にスペースを与えない場合
	if ( argc == 2 ) {
		while ( argv[1] != '\0' ) {

			if ( argv[1] == '+' ) {
				op = ( argv[1][1] );
			} 
			else if ( argv[1] == '-' ) {
				op = ( argv[1][1] );
			} 
			else if ( argv[1] == '*' ) {
				op = ( argv[1][1] );
			} 
			else if ( argv[1] == '/' ) {
				op = ( argv[1][1] );
			}

			if ( isdigit ( argv[1] ) ) {
				if ( argv[1] >= '0' && argv[1] <= '9' ) {

				}
			}
			i++;
		}
	}

	switch ( op ) {
		case '+':
			result = argv[1][0] + argv[1][2];
			break;
		case '-':
			result = argv[1][0] - argv[1][2];
			break;
		case '*':
			result = argv[1][0] * argv[1][2];
			break;
		case '/':
			result = argv[1][0] / argv[1][2];
			break;
		default:
			exit ( 0 );
	}

	printf ( "%d %c %d = %d\n", argv[1][0], op, argv[1][2], result );
}
 
 
 

今の所ソースはこんな感じです。お忙しい中何度もすみません。

管理人

Re:無題

#80

投稿記事 by 管理人 » 18年前

>argv[1]は文字列に対するポインターの配列なのに 
>argv[1][n]はどうして同じではないのでしょうか? 

え~と、
どうして同じではないのでしょうかということは、
argv[1][n]もアドレスが入っているとお思いでしょうか?
argv[1]は確かに[1][0]~[1][n]が示す配列のアドレスはいっています。
しかしargv[1][0]などは特定のデータそのものを示します。

今とんぷ~さんは

char st[10]="123";

という配列において、

なぜstと言う記述とst[1]という記述は同じではないのかとおっしゃっているのと同じです。
stというとst[0]~st[9]までのデータの集まりの先頭のアドレスを示しますよね。
st[1]というと、stが示すアドレスの1つ先にあるアドレスの中身、データ自身を示します。

int st[10]={1,2,3,};
printf("%d\n", st);
printf("%d\n",&st[0]);
printf("%d\n",&st[1]);
printf("%d\n",&st[2]);
printf("%d\n",&st[3]);
printf("%d\n", st[0]);
printf("%d\n", st[1]);
printf("%d\n", st[2]);
printf("%d\n", st[3]);

実行結果

1245004
1245004
1245008
1245012
1245016
1
2
3
0

それと同じように、**argvにおいて、
argv[1]はargv[1][0]からargv[1][n]までのひとまとまりの1つの配列の先頭アドレスを示します。
一方で
argv[1][1]やargv[1][n]はその中の配列の特定のデータ自身を示します。
argv[1][n]はアドレスが入っているわけではありません。

疑問に思うデータの中身を色々な形で表示させてみてください。
意味が解ると思います。
 
 

管理人

Re:無題

#81

投稿記事 by 管理人 » 18年前

コマンドラインから指定できる形に変更しました。
このプログラムは黄色い部分はサンプルと全く同じです。
コマンドラインから受け取った文字列をstにまとめる関数addを加えただけです。
これは途中にスペースがあろうと全部引数をstにadd関数でまとめてくれますからサンプルをそのまま利用することが出来ます。

「218 + 328」

「78* 219」

「9 /2」

など、計算可能です。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void add(int argc, char *argv[/url],char st[128]){
	int i=0,j=0,s=0;
	while( ++i < argc ){
		s=0;
		while(argv!='\0'){
			st[j]=argv;
			s++;
			j++;
		}
	}
	st[j]='\0';
}

int main(int argc, char *argv[/url]){

	int i=0,x[3];
	char st[128],c;

	add(argc,argv,st);

	while(st!='\0'){
		if( !(st>='0' && st<='9') ){
			c=st;
			st='\0';
			x[0]=atoi(st);
			x[1]=atoi(&st[i+1]);
			break;
		}
		i++;
	}

	printf("%d %c %d = ",x[0],c,x[1]);

	switch(c){
		case '+':	x[2]=x[0]+x[1];	break;
		case '-':	x[2]=x[0]-x[1];	break;
		case '*':	x[2]=x[0]*x[1];	break;
		case '/':	x[2]=x[0]/x[1];	break;
	}
	printf("%d",x[2]);
}

管理人

Re:無題

#82

投稿記事 by 管理人 » 18年前

仕様と異なる入力のエラー処理、0割禁止処理、小数点以下計算不可能など、改良するべき箇所は沢山あるので、サンプルを元に作られる場合は、各追加すべき処理を追加してください。

管理人

Re:無題

#83

投稿記事 by 管理人 » 18年前

後、とんぷ~さんの投稿されたコードは毎回私が直しているのですが、
コードを投稿するときは
<pre>と</pre>というタグで囲んでください。(「<」と「>」は半角で)
コードがこの方が綺麗に表示されますので。
プレタグを頻繁に利用するのでしたら、ユーザー辞書に追加するといいですよ。

@とんぷぅ~

Re:無題

#84

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。レスありがとうございました。

><pre>と</pre>というタグで囲んでください。
これは例えば
#include <stdio.h>
main()
{

文..............

return;
}
こういう解釈でいいのでしょうか?
ご迷惑をおかけして申し訳ありません。

@とんぷぅ~

Re:無題

#85

投稿記事 by @とんぷぅ~ » 18年前

すみません。分かりにくいですね。

ソースの一番上を
ソースの一番下を
で括るということですよね?

管理人

Re:無題

#86

投稿記事 by 管理人 » 18年前

それであってますよ^^
タグは見えなくなるので書いてもわかりませんよw
ですからカッコを半角でかかずに<pre>と書いたんですよ。

ウェブで使うタグの表示は普通こんな記号で書くのですが
http://www.tohoho-web.com/html/char.htm
ここの掲示板の使用でこの書き方は出来ないんですよね。

私は全員の記事編集できるので、編集画面を見ることが出来るのですが、そこでどのようにタグを使っているかわかるので私にはわかりますよ。

まぁそれでなくてもプレタグで囲むとスペースのあきかたやフォントが微妙にかわるのでわかりますけどね。
普段半角スペースは2回以上かくと1つにまとめられてしまいますが、
プレタグを使うとそのまま表示されます。タブインデントもね。

ですから、配列の事を説明するときに図を書きましたけど、あれもスペースをそのまま表示したいので、プレタグ使いました。

@とんぷぅ~

Re:無題

#87

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。レスありがとうございます。

>それであってますよ^^
安心しましたww

ちょっと作成してみたんですが減算が上手くいきません。
strtol関数の部分にあるprintf ( "error : Please input the integer\n" );
のメッセージが出て終了してしまいます。何がおかしいのでしょうか?
#pragma warning ( disable : 4996 )

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>

//	パラメータエラー
void usage ( void )
{
	printf ( "<< 引数を正しく入力して下さい。\n" );
	exit   ( 0 );
}

//	コマンドライン引数をst[128]にまとめる関数
void sto ( int argc, char *argv[/url], char st[128] )
{
	int i = 0, j = 0, s = 0;

	while ( ++i < argc ) {
		s = 0;
		while ( argv != '\0' ) {
			st[j] = ( argv );
			s++;
			j++;
		}
	}
	st[j]='\0';
}

//	main関数
int main ( int argc, char *argv[/url] )
{
	long long  i = 0;
	long long  mod;
	long long  lop;
	long long  rop;
	long long  result;

	char op;		//	演算子
	char st[128];	//	コマンドライン引数を格納
	char *check;

	//	パラメータのチェック
	if ( argc <= 1 || argc >= 5 ) {
		usage ();
	}
	
	//	関数呼び出し
	sto ( argc, argv, st );

	while ( st != '\0' ) {	
		if ( ! ( st >= '0' && st <= '9' ) && ( ! ( st == '-' ) ) ) {	

			op = st;		
			st = '\0';	

			lop = atoi (st);
			rop = atoi (&st[i+1]);

			break;
		}
		i++;
	}

	lop = strtol ( st, &check, 10 );
	if ( errno != ERANGE ) {
		if ( *check != '\0' ) {
			printf ( "error : Please input the integer\n" );
			exit   ( 0 );
		}

	} else {
		printf ( "error : The range of the int type is exceeded\n" );
		exit   ( 0 );
	}

	rop = strtol ( &st[i+1], &check, 10 );
	if ( errno != ERANGE ) {
		if ( *check != '\0' ) {
			printf ( "error : Please input the integer\n" );
			exit   ( 0 );
		}

	} else {
		printf ( "error : The range of the int type is exceeded\n" );
		exit   ( 0 );
	}
	

	//	演算子に沿って計算
	switch ( op ) {
		case '+':	
			result = lop + rop;	
			break;

		case '-':	
			result = lop - rop;	
			break;

		case '*':	
			result = lop * rop;	
			break;

		case '/':	
			if ( lop == 0 ) {
				printf ( "error : 0 division is a prohibition\n" );
				exit   ( -1 );
			}

			//	除算のオーバーフローを考慮
			if ( ( lop == INT_MIN ) && ( rop == -1 ) ) {
				printf ( "error : The overflow learns by experience\n" );
				exit   ( 0 );
			}

			result = lop / rop;	
			mod  = lop % rop;

			//	割り算の結果として余りが出なかった場合 
			if ( mod == 0 ) {
				printf ( "%lld %c %lld = %lld", lop, op, rop, result );
				exit   ( 0 );
					
			//	割り算の結果として余りが出た場合 
			} else if ( mod != 0 ) { 
				printf ( "%lld %c %lld = %lld 余り %lld", lop, op, rop, result, mod );
				exit   ( 0 );
			}
			
			break;
		default:
			printf ( "error : Please input either of operator '+', '-', '*', '/'\n" );
			exit   ( 0 );
	}
	
	//	加、減、乗算のオーバーフローを考慮
	if ( ( result > INT_MAX ) || ( result < INT_MIN ) ) {
		printf ( "error : The overflow learns by experience\n" );
		exit   ( 0 );
	}
	printf ( "%lld %c %lld = %lld", lop, op, rop, result );
}

管理人

Re:無題

#88

投稿記事 by 管理人 » 18年前

! ( st >= '0' && st <= '9' ) && ( ! ( st == '-' ) )

ここ、数字ではなく、マイナスではなければと書いてありますけど、
なぜマイナスの時演算子を取り込まないのですか?

! ( st >= '0' && st <= '9' )

このように書き直せばうまくいくと思います。

管理人

Re:無題

#89

投稿記事 by 管理人 » 18年前

後、long long型はstrtoll関数ではないでしょうか?

http://www.linux.or.jp/JM/html/LDP_man- ... tol.3.html

後、余りを出していますが、実数型で計算しないのでしょうか?
long double型など。
ただlong double型は処理系によっては無い可能性があるので、使わないほうがいいという考えもありますが。

@とんぷぅ~

Re:無題

#90

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。いつもお世話になっております。

>マイナスではなければと書いてありますけど、

先頭文字が'-'だった場合に、引き算と判定されてしまうので、
つけてみたんですが減算は上手くいきませんでした。

管理人さんの方でも試したのですが、今度は加算などでも
符号が違う場合などの計算が出来なくなってしまいました。

>後、long long型はstrtoll関数ではないでしょうか?

strtoll関数を使ったら以下のようなメッセージが出てコンパイル出来ませんでした。

1>c:\ct\src\projects\calc\calc\calc.c(69) : warning C4013: 関数 'strtoll' は定義されていません。int 型の値を返す外部関数と見なします。
1>リンクしています...
1>calc.obj : error LNK2019: 未解決の外部シンボル _strtoll が関数 _main で参照されました。
1>C:\CT\SRC\projects\calc\Debug\calc.exe : fatal error LNK1120: 外部参照 1 が未解決です。

管理人

Re:無題

#91

投稿記事 by 管理人 » 18年前

あぁ、マイナスはそういうことですか。じゃ、数字が出てくる前にマイナスがきたら・・という風に書かないと、マイナス自体読み込まなくなってしまいますよ。

strtollについては使ったことが無いので調べて見ます。

@とんぷぅ~

Re:無題

#92

投稿記事 by @とんぷぅ~ » 18年前

>あぁ、マイナスはそういうことですか。じゃ、数字が出てくる前にマイナスがきたら・・
>という風に書かないと、マイナス自体読み込まなくなってしまいますよ。

失礼しました。早速やってみます!!

管理人

Re:無題

#93

投稿記事 by 管理人 » 18年前

マイナスの件は今回は2つしか値を取り込まないので単純に

if ( ! ( st >= '0' && st <= '9' ) && i!=0 ) {

で回避できると思います。
最初にマイナスが来たときは許すと言うことは、判定を0以外の時、とすればいいのですから。

strtollの件ですが。。Visual Studio2005でもコンパイル通りません。
C99で追加された関数だと言うことはわかるのですが、VS2005の環境でもコンパイル出来ないとなると・・。
[strtoll() は C99 と POSIX.1-2001 に準拠している]と書いてあるので、
コンパイラがそれに準拠していないということではないでしょうか。

GCCはC99に準拠しているようですが、VS2005はC99に準拠していないということでしょうか。

Justy

Re:無題

#94

投稿記事 by Justy » 18年前

> strtoll
 MSVCなら "stdlib.h"に long longではないですけど
[color=#d0d0ff" face="aria[/url]
__int64 __cdecl _atoi64(const char *);
__int64 __cdecl _strtoi64(const char *, char **, int);
[/color]

 なんてのがありますよ。

@とんぷぅ~

Re:無題

#95

投稿記事 by @とんぷぅ~ » 18年前

管理人さん。Justyさん。レスありがとうございます。

私もvc2005なんですが、何やらstrtoll関数はvcでは使えないみたいですね。
何やら難しそうな話で私にはついていけません(笑)

ところで演算子を+/などと入れるとstrtol関数の部分で終了するため
違うエラーメッセージが出て困っています。そこで以下の文を入れたの
ですが、これですと1+-1=0等が弾かれてしまいます。何かよい方法は
ないでしょうか?

if ( strlen ( &st[i+1] ) >= 2 )

Justy

Re:無題

#96

投稿記事 by Justy » 18年前

> 違うエラーメッセージが出て困っています
 違うメッセージとはどういうことでしょう?
 st[i+1]が数値でなければならないところで、"/"であれば「数値を入力してほしい」というエラーで
合っていませんか?


> if ( strlen ( &st[i+1] ) >= 2 )
 これをどこに入れたのか、何を意味しているのかよくわからないです・・・。


> 1+-1
 ついに単項演算を条件に加えましたか。
 これを受け付けるようにする、ということは
 1+++++3とか 1-----4、1+-+-+-++---4も受け付けるのでしょうか?
(C言語的には ++/--演算子と混同しないように書けばOKだったりします)
[color=#d0d0ff" face="monospace] 
	int val1 = 1 + + + + + 3;
	int val2 = 1- - - - -4;
	int val3 = 1+-+-+-+-+-+-+-+-4;
	printf("%d, %d, %d\n", val1, val2, val3);[/color]

# ところで@とんぷ~さんに質問があります。

・ 最終仕様

 最終仕様の「括弧を含む演算」というのは "2 * (4 + 4)"のような複数の加減乗除を
扱う、ということでしょうか?


・ オーバーフローチェック
 今は内部の型が有限なのでチェックしているのだと思いますが、
最終的には(環境的な限界を抜きにして)無限桁になるので
今の段階でも不要なのではないでしょうか?


・ lop / rop

 whileループの中で代入して、その後 strtolでも代入しています。
 何故2回代入しているのでしょう?

@とんぷぅ~

Re:無題

#97

投稿記事 by @とんぷぅ~ » 18年前

Justyさん。レスありがとうございました。

>違うメッセージとはどういうことでしょう?
>st[i+1]が数値でなければならないところで、"/"であれば「数値を入力してほしい」

すみません。合ってました(笑)

>if ( strlen ( &st[i+1] ) >= 2 )
>これをどこに入れたのか、何を意味しているのかよくわからないです・・・。

すみません。これは1++1などでも計算できてしまうので
それを弾きたかったんですが、見当違いですみませんでした。
ちなみに"C言語的には ++/--演算子と混同しないように書けばOKだったりします"
とJustyさんがおっしゃられていますが、それなら1++1などが計算できてもいいのでしょうか?

>最終仕様の「括弧を含む演算」というのは "2 * (4 + 4)"のような複数の加減乗除を
>扱う、ということでしょうか?

はい。恐らくなんですが、そうなると思います。

>今は内部の型が有限なのでチェックしているのだと思いますが、
>最終的には(環境的な限界を抜きにして)無限桁になるので
>今の段階でも不要なのではないでしょうか?

現段階ではまずスペース有り、無しで計算出来、負数を括弧で括る条件
で一回見せないといけないのでこうしました。

>whileループの中で代入して、その後 strtolでも代入しています。
>何故2回代入しているのでしょう?

whileループの中の何処の部分でしょうか?ちょっと分かりませんでした。

長々と失礼しました。

Justy

Re:無題

#98

投稿記事 by Justy » 18年前

それなら1++1などが計算できてもいいのでしょうか
 個人的には出来てもいいかと思いますが、上司の方に確認してみたほうがいいでしょう。

 C言語がOKだからといって必ずしも "1 + + 1"を受け付ける必要はないかもしれませんが、
"1 + (+1)"は数学的にもOKなので受け付ける必要があるのではないかと思います。
 一応前に作った calc_testはどちらも出来るようになっています。


はい。恐らくなんですが、そうなると思います
 なるほど。
 となると、もうそろそろ今の解析方法とかだと厳しくなりそうですね。


現段階ではまずスペース有り、無しで計算出来、負数を括弧で括る条件
で一回見せないといけないのでこうしました
 なるほどそういうことでしたか。
 失礼しました。


whileループの中の何処の部分でしょうか
 わかりにくくてすみません。説明不足でした。
 
 えーと、whileループの中で
[color=#d0d0ff" face="monospace]    lop = atoi (st);
    rop = atoi (&st[i+1]);[/color]
 で lop/ropに代入し、それらを一度も使うことなく、
[color=#d0d0ff" face="monospace]lop = strtol ( st, &check, 10 );
rop = strtol ( &st[i+1], &check, 10 );[/color]
 と lop/ropに代入しているのでちょっと気になったのです。

 どちらかの代入が不要かと思います。

@とんぷぅ~

Re:無題

#99

投稿記事 by @とんぷぅ~ » 18年前

>負数を括弧で括る条件
ちなみに上記条件を実行するには字句解析いう方法
が必要なのでしょうか?
調べても中々参考になるものが無いので。。。

lop/ropの代入については直しました。指摘ありがとう
ございました。

Justy

Re:無題

#100

投稿記事 by Justy » 18年前

ちなみに上記条件を実行するには字句解析いう方法
 どっちかというと構文解析になりますが、最終的には必要になりますね。
 これをやらないと計算順序がわからないので、正しく計算できません。
 
 今の二項演算レベルであれば、力業で解決できるとは思いますが。

閉鎖

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