ページ 1 / 2
四則演算プログラムについて
Posted: 2007年5月22日(火) 13:55
by @とんぷぅ~
いつもお世話になっております@とんぷぅ~です。
今回も四則演算のプログラムなのですが、条件としては
①括弧を用いた計算を可能にする。
②スペースを入れなくても計算できるようにする。
③どんな整数を入力しても計算結果が正しく表示されるようにする。
というものなんですが、②については1 + 1 = 2を1+1=2でも計算
できるようにするということです。これはコマンドライン引数
から取得する方法でも可能なんでしょうか?
③については具体的に、桁の数を20や30にしても計算できるように
するということなんですが、これは型といったことを全く無視して
行うのでしょうか?どうすればよいのか全く思いつきません。
皆様よろしくお願いします。
Re:無題
Posted: 2007年5月22日(火) 15:53
by 管理人
まず、四則演算を計算するには、スタックが有効ではないかと思いますが、スタックの利用方法についてはよろしいでしょうか?
(どこから解説すればよいかが変わってきますので)
あと、スタックで計算する場合、
1 + 1
は
1 1 +
になりますが、後者のままではよくないのでしょうか?
昔似たような投稿がありました。
http://www.play21.jp/board/formz.cgi?ac ... q&rln=2980
Justyさんのお書きになったサンプルは時間がたったため自動削除されてしまったようですが、
スタックを使った四則演算プログラムならすぐ作れます。
Re:無題
Posted: 2007年5月22日(火) 16:27
by Justy
# @とんぷぅ~さん
>取得する方法でも可能なんでしょうか?
問題なく可能です。
複数のコマンドライン引数で与えられた計算式でも、1つの文字列に結合するか、
要素毎に分離するかしてしまえば楽に処理できるはずです。
>どうすればよいのか全く思いつきません
桁数が大きい数値の演算をするには、1つの手として多倍長整数を使うというのがあります。
ググれば出てきますので、調べてみてください。
# 管理人 さん
>時間がたったため自動削除
懐かしいスレですね。
あー、添付した物は過去ログ行きになると消えるんですね。
消えてくれると嬉しい物もあったりするので、助かるといえば助かります(w
Re:無題
Posted: 2007年5月22日(火) 16:40
by 管理人
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:無題
Posted: 2007年5月22日(火) 17:17
by @とんぷぅ~
管理人さん。レスありがとうございます。
申し訳ございませんが、スタックについては後入れ先出しの
データ構造ということしか理解しておりません。
11+というのは、引数に11+と入力し計算結果が2になる
ということなのでしょうか?
桁数についてはint型で扱うことが出来ない範囲も計算したい
(20桁どうしの掛け算など)のですが、文字列扱いにして
計算すれば良いのでしょうか?質問ばかりですみません。
Re:無題
Posted: 2007年5月22日(火) 17:29
by @とんぷぅ~
管理人さん。Justyさん。お世話になっております。
<<Justyさん
多倍長整数とは難しそうですが、調べてみます。
引数については結合か分離で可能なのですね。
ご回答ありがとうございました。
<<管理人さん
今回は一応何桁でも計算できるというのが目的で(あまり
長すぎてもアレなので、40桁まで扱いたいです。)
long long型だと扱える数値の数にどうしても、限りが
あるため、今回は使用出来ないのです。文章が分かりにくくて
すみませんでした。
Re:無題
Posted: 2007年5月22日(火) 19:58
by 管理人
あら・・、学校でスタックを使ったサンプルを作ってたのに、忘れてて帰ってきてしまった(T_T
すみません、、、。
しかし今回の課題を見るとスタックを使った方法では実現出来そうにないですね。
1 + 1
を
1 1 +
と入力する事になりますが、スペースをなくすと11になってしまいますし。
http://www.google.co.jp/search?hl=ja&q= ... %83%89&lr=
この辺を参考に調べてみてください。
私はスタックを利用した四則演算しかパッと思いつかないのですが、
サンプルをもう一度作ってみます。
後は全てカッコや四則演算子を全て個別に一つずつ解析していく方法しか思いつきませんが、
それは賢くないですよね。。
Re:無題
Posted: 2007年5月22日(火) 20:09
by box
数値用のスタックと演算子(カッコを含む)用のスタックを
別々に用意すれば、逆ポーランド記法を使わなくても、
一般的な数式の書き方に対応できるように思います。
Re:無題
Posted: 2007年5月22日(火) 20:24
by 管理人
スタックについてはご存知のようですので、そのまま
スタックを使った四則演算のサンプルを提示します。
以下のような計算式は
(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:無題
Posted: 2007年5月22日(火) 22:07
by @とんぷぅ~
管理人さん。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 );
}
長くなってしまって申し訳ありません。
Re:無題
Posted: 2007年5月22日(火) 23:01
by box
数値と演算子とを必ず空白で区切っているのであれば、
argv[/url] から取り出せますが、今行なおうとしているのは、
スペースがなくても(そして、あっても)
正しく計算できるようにすることですよね。
ということは、コマンドライン引数を使うのではなく、
fgets() か何かで1行分を取ってきて、
中にスペースがあったら無視して、
1文字ずつ見ていきながら数値と演算子に分けて、
スタックに格納しながら計算していく、という必要があるのではないでしょうか。
Re:無題
Posted: 2007年5月23日(水) 00:13
by 管理人
コマンドラインから行う方がよけいに面倒な処理になると思います。
どうしてもコマンドラインから行いたいなら複数にわかれている文字列を一度全部ひとまとまりにした方がいいかもしれません。
カッコを含むスタックを個別に用意する方法がよくわからないので、boxさんの方がずっといい回答をなさると思うのですが、
私は上記方法がダメなら、一つずつ個別に判定していく方法しか思いつきません、すみません(_ _|||)
Re:無題
Posted: 2007年5月23日(水) 00:52
by @とんぷぅ~
管理人さん。boxさん。レスありがとうございます。
<スペースがなくても(そして、あっても)
これはスペース無しだけの場合ならコマンドライン引数を
取得する方法で可能なのでしょうか?
<コマンドラインから行う方がよけいに面倒な処理になると思います
そうですよね。すごく大変そうです。明日もう一度確認してみますね。
<カッコを含むスタックを個別に用意する方法
私もこれを考えたのですが、どうコーディングしようか考え中です。
皆様の意見を参考にしながら、明日もう一度調べてみます。
そしてたぶんまた来ます(笑)
ちなみに、①~③まで一度に出来ればいいのでしょうけど、私には
難しそうなので、まずどの条件からクリアすればよいのかアドバイス頂ければ
幸いです(もしくは一度にやった方がよいのでしょうか?)
よろしくお願いします。
Re:無題
Posted: 2007年5月23日(水) 01:27
by Justy
>多倍長整数とは難しそうですが
たしかに多倍長整数を効率よく処理するルーチンをいきなり書くのは大変かもしれませんが、
多少効率を無視していいのなら比較的簡単です。
小学校の頃に加減乗除の筆算って習いませんでしたか?
加算なら縦に2つの数字を並べて下の桁から1桁ずつ対応した桁同士を処理し、
溢れたら溢れた分を次の桁に、ってアレです。
アレをそのままプログラムに落とし込めば・・・40桁限定なら、10進数での桁数、
各桁の数字(40個の配列)、符号の情報を構造体にして管理すればかなり簡単に
多倍長整数を扱えるはずです。
>引数については結合か結合
どちらかといったら結合の方がいいかもしれませんね。
ついでにスペースやタブなどの空白が含まれていたら削りながら
結合すると、必要な式の部分だけが残ってより解析しやすくなるはずです。
>スペース無しだけの場合ならコマンドライン引数を取得する方法で可能なのでしょうか?
式が入っている引数の数の分だけ文字列を結合するのが面倒であれば、
最悪、コマンドラインから与える式を ""(ダブルクォーテーション)で囲めば1つの
引数として式が main関数に入ってきますよ。
>①~③まで一度に出来ればいいのでしょうけど
>まずどの条件からクリアすればよいのか
んー、私なら、
1 まず本当にコマンドラインから式を受け取る必要があるのかどうか
必要があるなら、コマンドライン引数から文字列の結合を行い式を組み立てる。
必要がないなら、適当にユーザーに入力させる
2 桁数の問題
3 最後に括弧を含む式の解析。
ですね。
Re:無題
Posted: 2007年5月23日(水) 10:08
by @とんぷぅ~
Justyさん。レスありがとうございました。
大変参考になりました。出来るか不安ですが
とにかくやってみます。ありがとうございました。
管理人さん。boxさん。Justyさん。
お礼を忘れていました。すみませんでした。
Re:無題
Posted: 2007年5月23日(水) 13:45
by @とんぷぅ~
お世話になっております@とんぷぅ~です。やはりコマンドラインから
式を受け取る必要があり、スペース有りor無しの場合でも計算できるように
しないといけないみたいです^^;
Re:無題
Posted: 2007年5月23日(水) 13:58
by box
ということは、コマンドライン引数で受け取った文字列から
スペースを取り除いた形で1つの文字列にまとめ、
その文字列を1バイトずつ見ていき、
・'0'~'9' の場合
・'+', '-', '*', '/' の場合
・'(', ')' の場合
・その他の文字の場合
に分けて考えていけばよいと思います。
Re:無題
Posted: 2007年5月23日(水) 14:06
by @とんぷぅ~
boxさんレスありがとうございます。
<スペースを取り除いた形で1つの文字列にまとめ
すみません。この部分の意味が理解できません。どう
いったことなのでしょうか?
Re:無題
Posted: 2007年5月23日(水) 14:14
by box
<<スペースを取り除いた形で1つの文字列にまとめ
失礼しました。当方が勘違いしておりました。
複数のコマンドライン引数に分かれている文字列群には
スペースを含みません。
というわけで、sprintf()あたりを使って、単純に結合すればよいです。
Re:無題
Posted: 2007年5月23日(水) 14:25
by @とんぷぅ~
boxさん。度々ありがとうございます。
<sprintf()あたりを使って、単純に結合すればよいです。
sprintfで結合した場合、以下のような結果を求められる
ということですよね?
例.コマンドラインから受け取った引数が 1 * 2の3つの場合
引数に空白無し
ファイル名.exe 1*2 = 2
引数に空白有り
ファイル名.exe 1 * 2 = 2
Re:無題
Posted: 2007年5月23日(水) 15:15
by box
何だか話がちぐはぐになってきた気がしています。
>コマンドラインから受け取った引数が 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:無題
Posted: 2007年5月23日(水) 16:54
by @とんぷぅ~
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 );
}
この部分からおかしいということなのでしょうか?
混乱してきました。知識不足で申し訳ありません。
文章がおかしい部分も多々あると思いますが、よろしくお願いします。
Re:無題
Posted: 2007年5月23日(水) 17:13
by box
このスレッドの最初の投稿で、
「スペースを入れなくても計算できるようにしたい」旨
書かれていますね。
これと、
>それが引数にスペースを空けないで実行するということが
>やはり引っかかってきます。 例 1+1
これとは矛盾していませんか?
結局どうしたいのでしょうか?
Re:無題
Posted: 2007年5月23日(水) 17:40
by @とんぷぅ~
boxさん。レスありがとうございます。
分かりにくくてすみません。結局引数で1+1 としても、1△+△1としても
四則演算出来るようにしたいです(この場合ならどちらも2になるようにしたいということです)
(△はスペースです。)
Re:無題
Posted: 2007年5月23日(水) 20:37
by box
> 結局引数で1+1 としても、1△+△1としても
ということでしたら、私の本日15:15の投稿の
最後の3行をもう一度ごらんください。
Re:無題
Posted: 2007年5月24日(木) 00:18
by @とんぷぅ~
管理人さんタイトル変更ありがとうございました。
タイトルつけるの忘れてました^^;
boxさんレスありがとうございます。
文字列の結合、解析あたりが理解できていないのですね。
ご指摘ありがとうございました。頑張ります~。
Re:無題
Posted: 2007年5月24日(木) 01:46
by Justy
@とんぷぅ~さん、この課題は多分例によって上司の方に出されたものだと思うのですが、
この問題は少し敷居が高い感じがします。
前と同じように新人研修の課題ですか?
>どうしても結合するということが理解できません
もう既に理解されているかもしれませんが、一応簡単に。
[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:無題
Posted: 2007年5月24日(木) 03:07
by 管理人
バイトで遅くなりました・・((グッタリ
お力になれず、すみません、、。
あ、コマンドラインの受け取った文字列達をまとめると言うのは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:無題
Posted: 2007年5月24日(木) 08:56
by @とんぷぅ~
Justyさん。管理人さん。レスありがとうございました。
<前と同じように新人研修の課題ですか?
はい(。´Д⊂)今回は「今までより難しいよ」としっかり
言われておりました。まだポインタや構造体等etc理解出来て
いないので、Cのサイトと格闘中です。
結合について、具体的に書いていただきありがとうございました。
大変分かりやすかったです。
<バイトで遅くなりました・・((グッタリ
お疲れ様です。いつもお世話になってます。
サンプルありがとうございました。Justyさんにいただいた物も
含めて動かしてみます。
Re:無題
Posted: 2007年5月24日(木) 09:37
by 管理人
あ、途中配列要素数の所に++使っている部分が解り難かったかも知れませんので、
補足です。
以下を見てもらえたら解るとおり、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:無題
Posted: 2007年5月24日(木) 10:23
by @とんぷぅ~
管理人さん。レスありがとうございました。
「i++」は、iが評価されてからインクリメント
「++i」は、インクリメントされてからiが評価
この違いなのですね、基本ですがおろそかにしていました。
こういう細かい所も大変勉強なります。ありがとうございます。
Re:無題
Posted: 2007年5月24日(木) 13:40
by Justy
>はい(。´Д⊂)今回は「今までより難しいよ」としっかり 言われておりました
今時の新人研修はここまでやりますか・・・。
このレベルの課題は2,3年目くらいにならないと、なかなか作れるものじゃないですよ。
多分上司の方は「どこまで作れるか」「どう解決しようとしたのか」「何かできないのか」を
見ているのでしょうね。
Re:無題
Posted: 2007年5月24日(木) 13:49
by @とんぷぅ~
>多分上司の方は「どこまで作れるか」「どう解決しようとしたのか」「何かできないのか」見ている
思い通りに動くようになったら、コードを1文ずつ説明して、
どんな考えで作ったのか報告しなければならないので、
恐らくそういうことを見ているのだと思います。
まあ正直しんどいですが、頑張ります~。Justyさん。これからもよろしくお願いします。
Re:無題
Posted: 2007年5月24日(木) 15:11
by @とんぷぅ~
すみません。管理人さんのコードで分からない
部分があるのですが、コメントをいただけない
でしょうか?
for(i=1;i<argc;i++){
j=0;
while(argv[j]!=NULL)
st[s++]=argv[j++];
}
上記のwhile部分が具体的にどう連結に結びつくのでしょうか?
あとst='\0';配列をヌルを入力しているのはなぜなのでしょうか?
よろしくお願いします。
Re:無題
Posted: 2007年5月24日(木) 15:20
by 管理人
例えば今、
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:無題
Posted: 2007年5月24日(木) 15:29
by 管理人
サンプルです。
#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:無題
Posted: 2007年5月24日(木) 15:31
by 管理人
argcとargvの仕組みについては理解していますか?コマンドラインの入力文字がどのように格納されるか理解していますか?
Re:無題
Posted: 2007年5月24日(木) 15:43
by 管理人
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:無題
Posted: 2007年5月24日(木) 18:31
by @とんぷぅ~
管理人さん。レスありがとうございました。
argc、argvについて細かく調べた所、より理解が深まりました。
ただ、奥が深かったのでもう少し、ポインタ、配列、文字列の絡みを学習してきます。
特に多次元配列になるとテンパってしまって(汗)
p.s Justyさんが配布してくれたプログラムのような
動きに少しでも近づけるように頑張ります。
Re:無題
Posted: 2007年5月24日(木) 22:42
by Justy
>思い通りに動くようになったら、コードを1文ずつ説明して、
うわー、それはキッツイですね。
それじゃぁ、曖昧な部分があったら速攻で突っ込まれますからね、大変だ。
>Justyさんが配布してくれたプログラムのような
>動きに少しでも近づけるように頑張ります。
計算結果しか出していないんで、どれだけ参考になるかわかりませんが、
@とんぷ~さんのプログラムが完成した際の結果の検証くらいには使えるかと。
あ、そうそう。
ちょっとバグがあったので上のプログラムを更新してあります。
一見無限桁の演算ができるようにみえて、実は理論的に5万桁以上同士の乗算をしたときに
計算結果が希におかしくなる問題があったのでこっそり修正しました。
まだ何か結果がおかしいようでしたら言って下さい。
Re:無題
Posted: 2007年5月24日(木) 23:44
by @とんぷぅ~
Justyさんレスありがとうございました。
<ちょっとバグがあったので
そうなんですか!Justyさんのような方でも
バグがあるんでしたら私は。。。(笑)
更新ありがとうございました<(_ _)>
Re:無題
Posted: 2007年5月25日(金) 01:06
by 管理人
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:無題
Posted: 2007年5月28日(月) 10:52
by @とんぷぅ~
管理人さん。レスが遅れて申し訳ありませんでした。
質問があるのですが、例えば1+1として(スペース無し)の場合
argv[1]に1+1が入っているので、その文字列を解析して、数値なら
'1'と'+'と'1'という具合に解析をして計算させれば良いのでしょうか?
解析が出来ず、困っています。isdigit関数などを使用して数値かの
判別をすれば良いのでしょうか?質問ばかりで申し訳ありませんが
よろしくお願いします。
Re:無題
Posted: 2007年5月28日(月) 13:16
by 管理人
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]を調査、カッコでも数値でもないのでエラー終了。
こんな感じでどうでしょう?
Re:無題
Posted: 2007年5月28日(月) 13:39
by Justy
# @とんぷぅ~さんへ
>例えば1+1として(スペース無し)の場合
>argv[1]に1+1が入っているので、その文字列を解析して
argv[2]以降に一切「式」が入っていないことが保証できるのであれば、argv[1]を解析してください。
>数値なら '1'と'+'と'1'という具合に解析をして計算させれば良いのでしょうか?
基本的にはそうです。
ただ、括弧付きを扱うのであれば計算の順番を正しく判定する必要があります。
>isdigit関数などを使用して数値か判別をすれば良いのでしょうか?
数値かどうかなら yes。
# 管理人さんへ
>?st[8]を調査、カッコでも数値でもないのでエラー終了。
式が "123+456*-"ならエラーになりますが、"123+456*-5"ならエラーにはならないですよね?
Re:無題
Posted: 2007年5月28日(月) 14:18
by 管理人
Justyさん、
-5をかけるとき、
a*(-5)とかかないと数学的な式としておかしくないでしょうか?
私は数式でマイナスを書けるとき必ず括弧を書きますけど、省略できるんです?
しかし数学的な式として入力するなら
2a
という入力もありにしないといけなく・・。
どこまで実現するべきかは仕様によりますね。
もし*-という連続を許すならそこのエラー処理は必要ないと思います。
Re:無題
Posted: 2007年5月28日(月) 14:31
by @とんぷぅ~
管理人さん。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:無題
Posted: 2007年5月28日(月) 14:44
by 管理人
キーボードからの入力を必要としないならgetcharは必要ないでしょう。
もうすでに配列に文字列がはいっているのですから、配列要素を一つずつ調べたら同じ事です。
コマンドラインから可能かという質問は前にもお答えしましたよね?
複数にわかれた文字列を一つにするプログラムのサンプルもお書きしたと思います。
括弧なしの計算なら簡単です。
私の書いたスタックに合うように文字列をおいてやればいいだけです。
しかし括弧つきに応用が出来ませんから、もし括弧を実現すべきなのなら1からほかの考え方で作ったほうがよさそうです。
すみませんが、私には括弧つき計算でいい方法が思いつきません。
Re:無題
Posted: 2007年5月28日(月) 15:04
by Justy
# 管理人さん
>私は数式でマイナスを書けるとき必ず括弧を書きますけど、省略できるんです
一応省略しても「式」的には正しく読みとれそうですが。
たしかに言われてみれば "3*(-5)"では省略はしない方がいいのかもしれませんね。
でも "-5*3"の時は省略しますね。
う~む。この場合も括弧つけた方がいいでしょうか。"(-5)*3"みたいに
あとは()の前に付く場合ですね。 "4 * -(1 + 5)"とか。
>どこまで実現するべきかは仕様によりますね
ですね。
# @とんぷぅ~さん
>括弧や桁数を意識しないで、スペース有りor無しで普通に
>四則演算が出来るコードを書いてみようと思います
つまり括弧はなし、桁数はlong longの範囲で、スペースの有無はどちらでも、ということですね。
>コードを根本的に変えていかなければならないのでしょうか
最終的なコードは大幅に異なるものになりそうです。
が、上記の条件(括弧なしとか)であれば延長線上でいけそうな気がします。
>コマンドライン引数から読み込む ことは可能なのでしょうか?
今までどこから読み込んでいたのでしょうか?
Re:無題
Posted: 2007年5月28日(月) 15:50
by @とんぷぅ~
管理人さん。Justyさん。レスありがとうございます。
>もうすでに配列に文字列がはいっているのですから、配列要素を一つずつ調べたら
はい。その通りなのですが、その「調べる」ということが理解出来ておりません。
何が分かっていないからなのでしょうか?
>私の書いたスタックに合うように文字列をおいてやればいいだけ
スタックについてのサンプルありがとうございました。しかし今回
どうしても、自分でコーディングしたソースを元に作りたいのです。
(スペース有りor無しだけについて)
>コマンドライン引数から読み込む ことは可能なのでしょうか?
すみません。コピペでミスをしてしまいました。意味不明なことを
言ってすみませんでした。
>つまり括弧はなし、桁数はlong longの範囲で、スペースの有無はどちらでも、ということですね。
はい。そういうことになります。
>どこまで実現するべきかは仕様によりますね
明記するのを忘れておりました。すみません。
今回は 1 + 2 - 3などの3項演算?はしないので、括弧による優先順位は関係ありません。
括弧をつける基準としては入力された数値が負数の場合は括弧で括るということだけで大丈夫です。
>数値かどうかなら yes
isdigitを使ってargv[1]に入っている文字列を解析しようと
思ったのですが、中々上手くいきません。。
こんなに有用な意見を頂いているのにやりたいことがパッと出てこない為、
皆様に大変迷惑を掛けてしまってすみません。
Re:無題
Posted: 2007年5月28日(月) 18:24
by 管理人
すみません、ちょっと忙しくてお返事がちまちまになりそうです。
>しかし今回どうしても、自分でコーディングしたソースを元に作りたいのです。
人のプログラムを理解するのは大変ですし、自分で書いたほうが力になるので出来ることならそちらがいいと思います。
アルゴリズムはどのように考えています?
スタック使うのでしょうか?
> 1 + 2 - 3などの3項演算?はしないので
という意味がよくわからなかったのですが、今単純に「1+2」とか「3*2」とか3つ以上の数値は入力しないということなのでしょうか?(そんな簡単な事じゃないですよね^^;
>こんなに有用な意見を頂いているのにやりたいことがパッと出てこない為、皆様に大変迷惑を掛けてしまってすみません。
具体的なアドバイスが出来ていないので、わからなくても仕方ないと思いますし、遠慮なく情報交換しましょう。
結構最初の仕様を実現するのは難しいように思います。
Re:無題
Posted: 2007年5月28日(月) 18:42
by Justy
>括弧をつける基準としては入力された数値が負数の場合は括弧で括るということだけで大丈夫です。
なるほど、そこまでシンプルになれば相当簡単になりますね。
スタックも不要ですね。
2007/05/22(火) 22:07のソースから改良する、ということでしたが
この規模まで小さくなったら1から書き直した方が早そうな気もします。
スペース有りor無しでも動くように、文字列の結合から着手してみてはどうですか?
>isdigitを使ってargv[1]に入っている文字列を解析しようと思ったのですが、中々上手くいきません
どううまくいかないのでしょうか。
Re:無題
Posted: 2007年5月28日(月) 20:53
by @とんぷぅ~
管理人さん。Justyさん。レスありがとうございます。
"管理人さん"
>今単純に「1+2」とか「3*2」とか3つ以上の数値は入力しないということなのでしょうか?
はいそうです。こんなことで質問して申し訳ありません。
ちょっと課題が難しいのでまずここからやってみてっていう感じになりました。
>結構最初の仕様を実現するのは難しいように思います。
そうですね~。最終的には最初の仕様になると思います。
"Justyさん"
>スペース有りor無しでも動くように、文字列の結合から着手してみてはどうですか?
こちらは管理人さんから頂いたサンプルを使用しているのですが、単純に
sprintf ( str, "%s", argv[1] )等でも良いのでしょうか?はたまたstrcat等を
使用するのか?その辺が疑問です。
>isdigitとargvでは型が違うので、上手くいかずに困っています。argvに入っている
文字列を1文字ずつ確認するには違う関数を使うのでしょうか?もしくはargvに入っている
文字列を切り出してから、1文字ずつ解析を行うのでしょうか?トークンあたりを調べて
みたのですが、あまり理解出来ませんでした。
長々失礼しました。
Re:無題
Posted: 2007年5月28日(月) 21:00
by 管理人
とんぷ~さんの回答を拝見するに、ちょっとトークンなどのライブラリ関数を用いずに、
文字列と配列、アドレスや文字コードなどの勉強もかねて、配列1つ1つを操作するプログラムを書いた方がいいように思います。
ライブラリ関数を用いるより自分で配列の内容を確認しながら作っていった方が仕組みがよくわかりますしね。
ちょっと戻って1から基本を見つめなおしましょう。
文字列というものは配列要素[0]から'\0'が入っている配列要素までをひとまとまりとするとかその辺の事をしっかりおさらいしてください。
きっと基本があやふやだと応用を効かせようとしたとき無理が来てわからなくなってしまうと思いますので。
では
「1+1」
が文字列として存在するとき
「1」「+」「1」にわけて2次元配列に格納する事から始めましょう。
単純に四則演算だけならすごく簡単ですから。
Re:無題
Posted: 2007年5月28日(月) 21:14
by 管理人
サンプルを書きました。
入力はスペース無しで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:無題
Posted: 2007年5月28日(月) 21:24
by 管理人
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
Re:無題
Posted: 2007年5月28日(月) 22:11
by Justy
>単純に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:無題
Posted: 2007年5月29日(火) 16:51
by @とんぷぅ~
管理人さん。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:無題
Posted: 2007年5月29日(火) 17:40
by 管理人
>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:無題
Posted: 2007年5月29日(火) 17:51
by @とんぷぅ~
管理人さん。レスありがとうございます。
なんかこう配列名は先頭要素のアドレスという言葉で
分かった気になってしまっていました。大変分かり易い
説明ありがとうございました。もっと多次元配列のコンパイル
をして理解を深めてみます。
Re:無題
Posted: 2007年5月29日(火) 17:56
by 管理人
Re:無題
Posted: 2007年5月29日(火) 18:02
by Justy
>>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:無題
Posted: 2007年5月29日(火) 18:03
by @とんぷぅ~
早速のレスありがとうございます。typedefを使って定義されている型
とあったので、構造体が絡むんでしょうか?スミマセン。知識不足で
よく分かりませんでした。調べてみます。
Re:無題
Posted: 2007年5月29日(火) 18:10
by Justy
>typedefを使って定義されている型とあったので、構造体が絡むんでしょうか
とりあえず、size_tは要素の数を表現する型で、整数であるとだけ覚えておけば大丈夫です。
Re:無題
Posted: 2007年5月29日(火) 18:29
by @とんぷぅ~
Justyさん。レスありがとうございます。
>これは char **argvと扱い的には同じになります。
>つまりポインタのポインタです。
ポインタのポインタ。。。言葉を聞くと一瞬泣きそうになります(笑)
>もちろん、理論的には連結しなくてもできますが、ちょっとだけ面倒になります。
ですよね。回答ありがとうございました。
>例えばargv[1]じゃなくて、argv[2]の解析をしようとか考えたとき、
>ソースのあっちこっちに argv[1]が散乱していたら
>書き換えるのが面倒ですよね?
確かにあっちこっちに散乱してる場合は面倒ですね。
ちなみに、流れ的にはargv[1]~argv[3]までを連結し、その上で
argv[/url]に入っている文字列を1文字ずつ解析し、数値なら式に沿って演算させる。
というふうな感じにすればいいんですよね?(こちらが質問者なのにすみません)
Re:無題
Posted: 2007年5月29日(火) 19:57
by 管理人
>ポインタのポインタ。。。言葉を聞くと一瞬泣きそうになります(笑)
苦手意識をお持ちだという事が伝わってきますが、全然難しいものではないですよ。
例えば、ポインタとは目印だと思ってください。
データには目印がついていて、その目印をみればデータが取り出せると。
char **st;
で受け取ればポインタのポインタという事になりますが、
言い換えれば
目印が入った連続した箱の集まり、その集まりにさらに目印をつけたものの箱の集まり、ということです。
ポインタというのは言ってみれば配列です。
char a[/url]="1234";
とかいても
char *a="1234";
と書いてもいいことご存知でしょか。
上記両方ともaとかけばaのアドレスの事を示します。
2重ポインタということは、これらの配列の先頭アドレスを一つずつ格納した配列の集まりと言う事だと言い換えられます。
2重ポインタと2次元配列は同じではなく、意味は違うのですが、同じ意味も示す事が出来ます。
う~ん、、言ってて余計にわからなくさせてしまったかな・・・。
Re:無題
Posted: 2007年5月29日(火) 20:11
by @とんぷぅ~
管理人さん。レスありがとうございます。
配列とポインタは似て非なるものと見ました。ポインタと
配列の決定的に違う部分というのは、本を見て分かったのですが
本当に理解したとは言えません(泣)
しかも本によっての解説が違うので今いちでした。
Re:無題
Posted: 2007年5月29日(火) 23:04
by Justy
>流れ的にはargv[1]~argv[3]までをを連結
argcの値によって変わりそうですが、基本的にその通りです。
>argv[/url]に入っている文字列を1文字ずつ解析し/i>
argv[/url]に、というか連結して作った文字列に対して、ですね。
>数値なら式に沿って演算させる
yes.
Re:無題
Posted: 2007年5月29日(火) 23:28
by @とんぷぅ~
Justyさんレスありがとうございます。
>argcの値によってargv[1]~argv[3]までをを連結
今回は2項演算までなので最大でもargv[3]までが引数に
なので、argcは最大で4になると思うのですが、考え方
は正しいでしょうか?
>argv[/url]に、というか連結して作った文字列に対してですね。
その通りですね。連結した文字列に対して1文字ずつ解析ですね。
しかし、自分のやりたいことをコードにするのは難しいですね。
この頃コンパイラに怒られてばかりです(笑)
Re:無題
Posted: 2007年5月29日(火) 23:49
by Justy
>argcは最大で4になると思うのですが、考え方は正しいでしょうか?
正しいです。
>自分のやりたいことをコードにするのは難しいですね
何かこう、プログラムの本質みたいなのが見えてくると、そのあたりはぐっと簡単になりますよ。
その壁を越えるまでが大変ですけど・・・。
Re:無題
Posted: 2007年5月30日(水) 00:01
by @とんぷぅ~
Justyさん。レスありがとうございます。
>何かこう、プログラムの本質みたいなのが見えてくると、そのあたりはぐっと簡単になりますよ。
>その壁を越えるまでが大変ですけど・・・。
確かにそうですね。いつになれば自分で「コーディングした」っていう
感覚で仕事が出来るのか。。。。壁は高いです。。。。(( T_T)トボトボ
Re:無題
Posted: 2007年5月30日(水) 01:02
by 管理人
ちょっと意欲低下してらっしゃるようなので気分転換のコメントでも^^;
@とんぷぅ~さんはきっと現在「やらされているプログラミング」であったり、「しなければならないからやっているプログラミング」であるのではないでしょうか。そのために
なかなか思い通りに行かない・・(_ _|||)ズ~ン
見たいな心境になってしまっているのではと思いますけど、プログラムを趣味で楽しいと思ってやっているとプログラムの見方が全然違ってきますよ。
昔ポインタが嫌いでした、構造体が嫌いでした、文字列処理が嫌いでした、自作関数がわかりませんでした。
しかしゲームをちょびちょび作っていく中でそこを嫌っていたらそれ以上の処理が出来ない事に気が付きました。
もっとよいゲームを作りたい、もっとコードを賢く書きたい、もっと効率よいコードが書いてみたい。
そんな思いで色々な単元を勉強していきました。
ちょっと勉強してはゲームを実装し、ちょっと勉強してはゲームを実装しました。
私は参考書をひたすら読んで勉強した覚えなど一度もありません。(だから基礎がなってないんだといわれたらそれまでデスガ^^;)
必要な時に調べては、コーディングするという繰り返しでした。
(そうするといつも知ってる知識内でしかプログラムコードをかかなくなってしまっていたので、意図的に他のアルゴリズムを勉強したりしたことはありましたが)
私はゲームをプログラムで作る事が楽しかったので、難しいな嫌だなと思う単元も、苦に思うことなく勉強できました。
というか趣味だったので、勉強という感じがありませんでした。
それに、きっと読むばっかりの勉強ではなく、それを勉強したらすぐにそれを使ってコーディングしたのが良かったのだと思います。
数学の教科書を1冊最初から最後まで読み通してわかった気になって、いざ教科書をひらかずに練習問題やろうとしたらさっぱりできなかったというのと同じように、
プログラムも演習によって身に付いていくものです。
また、「おもしろい!」と思えばそれに勝るものはありません。
プログラムのおもしろさを先に見つけ、プログラムの面白さを実感しながら、新しい単元を演習によって身につける道が最短コースだと思います。
((といってもシゴトとかで必要なのでしたらのんびりゲームなんて作ってる暇ないですかね^^;
もしどうしてもいやになったら、今勉強している範囲を利用して小さなゲームを作ってみたりアプリを作ってみたりして自分で学習意欲を出させる事も大事かと思います。
もし時間が出来たら色んな方向からプログラムをみつめてみてください☆
・・と偉そうな事を言ってみてもまだ私はプログラムのプの字もわかってないわけですが^^;
Re:無題
Posted: 2007年5月30日(水) 09:33
by @とんぷぅ~
管理人さん。レスありがとうございます。
<プログラムのおもしろさを先に見つけ、プログラムの面白さを実感しながら、
<新しい単元を演習によって身につける道が最短コースだと思います。
ですね。今は楽しむではなく、やらされている感が先行してしまいますね。
もっとこう自分で楽しく作れるようになるといいんですがw
<・・と偉そうな事を言ってみてもまだ私はプログラムのプの字もわかってない
いえいえそんなことはありません。管理人さんの説明は分かりやすいですし
私からみたら雲の上の存在です。まぁ私からすればここのサイト
にいる方が皆さんそうでしw
Re:無題
Posted: 2007年5月30日(水) 11:43
by TT414
>どんな整数を入力しても計算結果が正しく表示されるようにする。
不可能です。無限大のメモリまたはハードディスクがなければできません。
必ず、制限があります。
問題を出題した人に正解を聞いてみましょう(どんな答えを出すか楽しみ)。
Re:無題
Posted: 2007年5月30日(水) 13:58
by @とんぷぅ~
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:無題
Posted: 2007年5月30日(水) 14:28
by 管理人
それは「文字コード」+「文字コード」をしているからです。
受け取ったものは「文字」であり、「数値」ではありません。
「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:無題
Posted: 2007年5月30日(水) 15:31
by @とんぷぅ~
管理人さん。レスありがとうございます。
atoiで数値に変換したいのですがargv[1][0]等とすると
コンパイルできません。文字を解析して格納までが上手くいきません。
後、ソースでこうした方がいいということはどんどん指摘して
下さい。よろしくお願いします。
Re:無題
Posted: 2007年5月30日(水) 15:47
by 管理人
関数が利用できない
↓
自分の関数の使い方が間違っているのかな?
↓
それなら関数の仕様を調べてみよう
こういう流れで利用できない関数は利用できるようにしていきましょう。
まず、atoiの関数の仕様を調べてみることです。
渡すべき変数の形と現在渡している変数の形がちゃんとあっていますか?
>文字を解析して格納までが上手くいきません。
文字列を解析して数値に変換すべきものは数値に変換し、中の演算子は別途格納するサンプルは既に示しました。
サンプルをじっくりみてください。
Re:無題
Posted: 2007年5月30日(水) 16:06
by @とんぷぅ~
>渡すべき変数の形と現在渡している変数の形がちゃんとあっていますか?
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:無題
Posted: 2007年5月30日(水) 16:54
by 管理人
>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:無題
Posted: 2007年5月30日(水) 17:12
by 管理人
コマンドラインから指定できる形に変更しました。
このプログラムは黄色い部分はサンプルと全く同じです。
コマンドラインから受け取った文字列を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:無題
Posted: 2007年5月30日(水) 17:16
by 管理人
仕様と異なる入力のエラー処理、0割禁止処理、小数点以下計算不可能など、改良するべき箇所は沢山あるので、サンプルを元に作られる場合は、各追加すべき処理を追加してください。
Re:無題
Posted: 2007年5月30日(水) 17:19
by 管理人
後、とんぷ~さんの投稿されたコードは毎回私が直しているのですが、
コードを投稿するときは
<pre>と</pre>というタグで囲んでください。(「<」と「>」は半角で)
コードがこの方が綺麗に表示されますので。
プレタグを頻繁に利用するのでしたら、ユーザー辞書に追加するといいですよ。
Re:無題
Posted: 2007年5月30日(水) 17:30
by @とんぷぅ~
管理人さん。レスありがとうございました。
><pre>と</pre>というタグで囲んでください。
これは例えば
#include <stdio.h>
main()
{
文..............
return;
}
こういう解釈でいいのでしょうか?
ご迷惑をおかけして申し訳ありません。
Re:無題
Posted: 2007年5月30日(水) 17:32
by @とんぷぅ~
すみません。分かりにくいですね。
ソースの一番上を
ソースの一番下を
で括るということですよね?
Re:無題
Posted: 2007年5月30日(水) 19:23
by 管理人
それであってますよ^^
タグは見えなくなるので書いてもわかりませんよw
ですからカッコを半角でかかずに<pre>と書いたんですよ。
ウェブで使うタグの表示は普通こんな記号で書くのですが
http://www.tohoho-web.com/html/char.htm
ここの掲示板の使用でこの書き方は出来ないんですよね。
私は全員の記事編集できるので、編集画面を見ることが出来るのですが、そこでどのようにタグを使っているかわかるので私にはわかりますよ。
まぁそれでなくてもプレタグで囲むとスペースのあきかたやフォントが微妙にかわるのでわかりますけどね。
普段半角スペースは2回以上かくと1つにまとめられてしまいますが、
プレタグを使うとそのまま表示されます。タブインデントもね。
ですから、配列の事を説明するときに図を書きましたけど、あれもスペースをそのまま表示したいので、プレタグ使いました。
Re:無題
Posted: 2007年5月31日(木) 14:51
by @とんぷぅ~
管理人さん。レスありがとうございます。
>それであってますよ^^
安心しました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:無題
Posted: 2007年5月31日(木) 15:20
by 管理人
! ( st >= '0' && st <= '9' ) && ( ! ( st == '-' ) )
ここ、数字ではなく、マイナスではなければと書いてありますけど、
なぜマイナスの時演算子を取り込まないのですか?
! ( st >= '0' && st <= '9' )
このように書き直せばうまくいくと思います。
Re:無題
Posted: 2007年5月31日(木) 15:26
by 管理人
後、long long型はstrtoll関数ではないでしょうか?
http://www.linux.or.jp/JM/html/LDP_man- ... tol.3.html
後、余りを出していますが、実数型で計算しないのでしょうか?
long double型など。
ただlong double型は処理系によっては無い可能性があるので、使わないほうがいいという考えもありますが。
Re:無題
Posted: 2007年5月31日(木) 15:54
by @とんぷぅ~
管理人さん。いつもお世話になっております。
>マイナスではなければと書いてありますけど、
先頭文字が'-'だった場合に、引き算と判定されてしまうので、
つけてみたんですが減算は上手くいきませんでした。
管理人さんの方でも試したのですが、今度は加算などでも
符号が違う場合などの計算が出来なくなってしまいました。
>後、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:無題
Posted: 2007年5月31日(木) 15:58
by 管理人
あぁ、マイナスはそういうことですか。じゃ、数字が出てくる前にマイナスがきたら・・という風に書かないと、マイナス自体読み込まなくなってしまいますよ。
strtollについては使ったことが無いので調べて見ます。
Re:無題
Posted: 2007年5月31日(木) 16:11
by @とんぷぅ~
>あぁ、マイナスはそういうことですか。じゃ、数字が出てくる前にマイナスがきたら・・
>という風に書かないと、マイナス自体読み込まなくなってしまいますよ。
失礼しました。早速やってみます!!
Re:無題
Posted: 2007年5月31日(木) 16:49
by 管理人
マイナスの件は今回は2つしか値を取り込まないので単純に
if ( ! ( st >= '0' && st <= '9' ) && i!=0 ) {
で回避できると思います。
最初にマイナスが来たときは許すと言うことは、判定を0以外の時、とすればいいのですから。
strtollの件ですが。。Visual Studio2005でもコンパイル通りません。
C99で追加された関数だと言うことはわかるのですが、VS2005の環境でもコンパイル出来ないとなると・・。
[strtoll() は C99 と POSIX.1-2001 に準拠している]と書いてあるので、
コンパイラがそれに準拠していないということではないでしょうか。
GCCはC99に準拠しているようですが、VS2005はC99に準拠していないということでしょうか。
Re:無題
Posted: 2007年5月31日(木) 17:21
by Justy
> 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:無題
Posted: 2007年5月31日(木) 17:47
by @とんぷぅ~
管理人さん。Justyさん。レスありがとうございます。
私もvc2005なんですが、何やらstrtoll関数はvcでは使えないみたいですね。
何やら難しそうな話で私にはついていけません(笑)
ところで演算子を+/などと入れるとstrtol関数の部分で終了するため
違うエラーメッセージが出て困っています。そこで以下の文を入れたの
ですが、これですと1+-1=0等が弾かれてしまいます。何かよい方法は
ないでしょうか?
if ( strlen ( &st[i+1] ) >= 2 )
Re:無題
Posted: 2007年5月31日(木) 23:59
by Justy
> 違うエラーメッセージが出て困っています
違うメッセージとはどういうことでしょう?
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:無題
Posted: 2007年6月01日(金) 14:20
by @とんぷぅ~
Justyさん。レスありがとうございました。
>違うメッセージとはどういうことでしょう?
>st[i+1]が数値でなければならないところで、"/"であれば「数値を入力してほしい」
すみません。合ってました(笑)
>if ( strlen ( &st[i+1] ) >= 2 )
>これをどこに入れたのか、何を意味しているのかよくわからないです・・・。
すみません。これは1++1などでも計算できてしまうので
それを弾きたかったんですが、見当違いですみませんでした。
ちなみに"C言語的には ++/--演算子と混同しないように書けばOKだったりします"
とJustyさんがおっしゃられていますが、それなら1++1などが計算できてもいいのでしょうか?
>最終仕様の「括弧を含む演算」というのは "2 * (4 + 4)"のような複数の加減乗除を
>扱う、ということでしょうか?
はい。恐らくなんですが、そうなると思います。
>今は内部の型が有限なのでチェックしているのだと思いますが、
>最終的には(環境的な限界を抜きにして)無限桁になるので
>今の段階でも不要なのではないでしょうか?
現段階ではまずスペース有り、無しで計算出来、負数を括弧で括る条件
で一回見せないといけないのでこうしました。
>whileループの中で代入して、その後 strtolでも代入しています。
>何故2回代入しているのでしょう?
whileループの中の何処の部分でしょうか?ちょっと分かりませんでした。
長々と失礼しました。
Re:無題
Posted: 2007年6月01日(金) 15:09
by Justy
>それなら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:無題
Posted: 2007年6月01日(金) 17:12
by @とんぷぅ~
>負数を括弧で括る条件
ちなみに上記条件を実行するには字句解析いう方法
が必要なのでしょうか?
調べても中々参考になるものが無いので。。。
lop/ropの代入については直しました。指摘ありがとう
ございました。
Re:無題
Posted: 2007年6月01日(金) 18:39
by Justy
>ちなみに上記条件を実行するには字句解析いう方法
どっちかというと構文解析になりますが、最終的には必要になりますね。
これをやらないと計算順序がわからないので、正しく計算できません。
今の二項演算レベルであれば、力業で解決できるとは思いますが。