コマンドラインから入力するプログラムについて質問です

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

コマンドラインから入力するプログラムについて質問です

#1

投稿記事 by ぽん » 17年前

久しぶりに投稿します<ぽん>です。
いま、コマンドラインからの入力という問題に挑戦中なのですが、ダブルポインタなどがでてきてしまい、作業が途中で止まってしまっています。
教えてくださいm(>_<)m

***
コマンドラインから入力された文字列に小文字のアルファベットが含まれていれば、すべて大文字に変え、文字列を昇順に並べ替えて表示させましょう。
パラメータ文字列が2つ以上入力されなければエラーメッセージを表示して終了させること。

>>sample Ichi
と入力した場合
Usage : STR1 STR2 [STR3 ... ]

>>sample matsuki harada
と入力した場合
** Str data **
matsuki
harada

** Changed str **
HARADA
MATSUKI

>>sample no3 no2 no1
と入力した場合
** Str data **
no3
no2
no1

** Changed str **
NO1
NO2
NO3

というように実行させたいのですが、ソートと大文字への変換がうまくできません。
#include<stdio.h>

int main(int argc, char *argv[/url])
{
	int i;
	if(argc < 2)
	{
		printf("\nUsage %s STR1 STR2 [STR3 ... ]\n",argv[0]);
		return 1;
	}

	printf("\n** Str data **");
	
	for(i = 1; i < argc; ++i)
	{
		printf("\n%s",argv);
	}

	printf("\n\n");
	
	void change(int, char **);
	void sort(int, char **);

	printf("** Changed str **");

	for(i = 1; i < argc; ++i)
	{
		printf("\n%s",argv);
	}
	
	printf("\n");

	return 0; 
}

void change(int cnt, char **pp)
{
	int i;
	char *p;
	
	for(i = 1; i < cnt; i ++)
	{
		for(p = pp; *p !='\0'; p++)
		{
			if((*p>='a')&&(*p<='z'))
			{
				*p = *p - 'a' + 'A';
			}
		}
	}
}

void sort(int cnt, char **pp)
{
	int i;
	int j;
	int k;
	char *p;

	for(i = 0; i < cnt-1; i++)
	{ 
		for(j = cnt-1; 1 + i < j; j--)
		{
			for(k = 0; pp[j][k] == pp[j-1][k]; k ++);
			{
				if(pp[j][k] < pp[j-1][k])
				{
					p = pp[j];
					pp[j] = pp[j-1];
					pp[j-1] = p;
				}
			}
		}
	}
}

***
いまのところ作ってみているのが上記のものです。
void change(int, char **);
void sort(int, char **);
の部分が間違っているのだろうとは思うのですが・・・どうでしょうか?
ソートと変換の部分もかなり怪しいですが;

<!--12-->

box

Re:コマンドラインから入力するプログラムについて質問です

#2

投稿記事 by box » 17年前

言語はC++ですか?Cだと、

>	void change(int, char **);
>	void sort(int, char **);

ここのところで、「ここで宣言はできない」という内容の
コンパイルエラーが出るはずですからね。

というわけで、上記はプロトタイプ宣言の書き方です。
change関数とsort関数を宣言しているだけで実行していません。

両関数の内容そのものは正しいようです。
main関数から、正しく呼び出してください。

ぽん

Re:コマンドラインから入力するプログラムについて質問です

#3

投稿記事 by ぽん » 17年前

ああ!そうでした;
エラーが出ていたのに記入しておりませんでした;
おまけに呼び出し忘れに気づかずに・・・すみません;
言語はCです。

ありがとうございます、きちんと実行できました!
ところで、質問なんですが・・・

> void change(int, char **);
> void sort(int, char **);

この部分はmainの中に書くよりも、外に出した方がいいのでしょうか?

まだまだC言語の勉強が足りてないので・・・今回のプログラムもエラーを直しながら作成しているので、ソースの理解が出来ておらず。。。

もし何か他に気づいたことやご指摘があれば、よろしければ教えてくださいm(__)m

box

Re:コマンドラインから入力するプログラムについて質問です

#4

投稿記事 by box » 17年前

プロトタイプ宣言を書くのであれば、ソースファイルの先頭付近で、
すべての関数の外側に来るように書くのがよくあるやり方です。
プライベート・ヘッダファイルにまとめて書いたりもします。

また、プロトタイプ宣言を書かずにすませる方法もあります。
今回の例であれば、

change関数
sort関数
main関数

あるいは

sort関数
change関数
main関数

の順に書く方法です。
要するに、ある関数を呼び出すよりも「前」に、
当該関数のプロトタイプ宣言または実体があればよいです。

ただし、プロトタイプ宣言を書かずにすませる方法の場合、
関数の実体を書く順序によってはコンパイル時に
エラーあるいは警告が出ます。例えば、仮に、change関数の中で
sort関数を呼び出すようになっていたとすると、

sort関数
change関数
main関数

の順に書けばエラーや警告は出ません。しかし、

change関数
sort関数
main関数

の順に書くと、コンパイル時、コンパイラがchange関数の中で
sort関数の呼び出しを発見したときに
sort関数の引数や戻り値の型がその時点では不明なため、
エラーあるいは警告が出ます。

プロトタイプ宣言を書くか、書かずにすませるかは、
個人の流儀であったり開発チーム内の規約であったりします。
どちらの方法がいいとも悪いともいえないと思いますが、
少なくとも、一つのプログラムの中では混在させない方がよいでしょう。
軸を決めたらぶれさせない、という考えです。

ぽん

Re:コマンドラインから入力するプログラムについて質問です

#5

投稿記事 by ぽん » 17年前

なるほど~。
ありがとうございます、とても分かりやすくて勉強になります!

もう少し、お時間あれば教えてください。
配列の先頭アドレスを指定する際に、【&配列名[0]】という形で書きたいのですが下記の①~⑤の部分は、こういった形に出来ないものでしょうか?
①は&を付け加えても問題なかったのですが、②以降はエラーがでたり実行がうまくいきませんでした。
【&配列名[0]】の形では書けないと言う事でしょうか。

それと、ポインタ変数を扱う際に、配列([ ])として扱わず、間接参照演算子(*)を用いて書きたいのですがこのプログラムではどこの部分に当たるのでしょうか?

質問ばかりしてスミマセン;
自分でもやってみるんですが途中でこんがらがってしまいました;

***
#include<stdio.h>

void change(int, char **);
void sort(int, char **);

int main(int argc, char *argv[/url])
{
	int i;
	int cnt;
	int pp;

	if(argc <= 2)
	{
		printf("\nUsage %s STRING1 STRING2 [STRING3 ... ]\n",&argv[0]); /*①*/
		return 1;
	}

	printf("\n** String data **");
	
	for(i = 1; i < argc; ++i)
	{
		printf("\n%s",argv);  /*②*/
	}

	printf("\n\n");

	change(argc, argv);  /*③argc,argvどちらも*/
	sort(argc, argv);   /*④argc,argvどちらも*/

	printf("** Changed string **");

	for(i = 1; i < argc; ++i)
	{
		printf("\n%s",argv);  /*⑤*/
	}
	
	printf("\n");

	return 0; 
}

void change(int cnt, char **pp)
{
	int i;
	char *p;
	
	for(i = 1; i < cnt; i ++)
	{
		for(p = pp; *p !='\0'; p++)
		{
			if((*p>='a')&&(*p<='z'))
			{
				*p = *p - 'a' + 'A';
			}
		}
	}
}

void sort(int cnt, char **pp)
{
	int i;
	int j;
	int k;
	char *p;

	for(i = 0; i < cnt-1; i++)
	{ 
		for(j = cnt-1; 1 + i < j; j--)
		{
			for(k = 0; pp[j][k] == pp[j-1][k]; k ++);
			{
				if(pp[j][k] < pp[j-1][k])
				{
					p = pp[j];
					pp[j] = pp[j-1];
					pp[j-1] = p;
				}
			}
		}
	}
}

***

組木紙織

Re:コマンドラインから入力するプログラムについて質問です

#6

投稿記事 by 組木紙織 » 17年前

>【&配列名[0]】の形では書けないと言う事でしょうか。
【配列名】と【&配列名[0]】はどちらも配列の先頭要素へのポインタを返しています。
よって【配列名】と表記可能な部分はすべて【&配列名[0]】に置き換え可能です。

ここで問題にしているのは、change()の引数のchar**の部分を【&配列名[0]】に表記
したいということでしょうか?

argvはchar*の配列と宣言されているので、その先頭要素へのポインタはchar**と同じ型になり
求めている形で表現可能です。
argcはint型なので(もともと配列ではないし)求めている形で表現は型が一致しないので不可能です。


(型の自動変換についての言及は省いています。)


>それと、ポインタ変数を扱う際に、配列([ ])として扱わず、間接参照演算子(*)を用いて書きたいのですがこのプログラムではどこの部分に当たるのでしょうか?

直接の答えにはなりませんが、ヒントとしてはこんな感じです。
(してはいけないような書き方を一部お遊びでしているので真似をしないでください。)
#include <stdio.h>

int main(void)
{
	int a[3],i;
	for(i=0;i<3;i++)
		printf("&a[%d]: %p\n",i,&a),
		printf("(a+%d): %p\n",i,(a+i)),
		printf("\n");
 	return 0;
}


# ソースコードの中に全角空白はやめてほしいと思います。

ぽん

Re:コマンドラインから入力するプログラムについて質問です

#7

投稿記事 by ぽん » 17年前

ありがとうございます。
ヒントを参考に直してみました。
エラーはでませんが、実行結果でソートがされなくなってしまいました;
ソート部分を([/url])を使った書き方から(*)を使う書き方に直したかったのですが、直し方がダメだったのでしょうか?(ポインタのポインタを使用した場合の直し方がいまいち理解できていなくて;)
void sort(int cnt, char **pp)
{
	int i;
	int j;
	int k;
	char *p;

	for(i = 0; i < cnt-1; i++)
	{ 
		for(j = cnt-1; 1 + i < j; j--)
		{
			for(k = 0; *(*pp+j+k) == *(*pp+(j-1)+k); k ++);
			{
				if(*(pp+j+k) < *(pp+(j-1)+k))
				{
					*p = *(*pp+j);
					*(*pp+j) = *(*pp+(j-1));
					*(*pp+(j-1)) = *p;
				}
			}
		}
	}
}
実行例:
>>sample matsuki harada
と入力した場合
** Str data **
matsuki
harada

** Changed str **
MATSUKI
HARADA ←本来なら「HARADA」が先に来て「MATSUKI」が後に来ないといけないのですが。

box

Re:コマンドラインから入力するプログラムについて質問です

#8

投稿記事 by box » 17年前

3重ループではなく、2重ループでできるのではないか、
と思います。
また、

> 			for(k = 0; *(*pp+j+k) == *(*pp+(j-1)+k); k ++);

最後のセミコロンは、何を意味しているのでしょうか?

ぽん

Re:コマンドラインから入力するプログラムについて質問です

#9

投稿記事 by ぽん » 17年前

すみません!
書き間違いですね、セミコロンは必要なかったです;
2重ループ・・・for文を減らすことができるということでしょうか。(どうすれば・・・;)
void sort(int cnt, char **pp)
{
	int i;
	int j;
	int k;
	char *p;

	for(i = 0; i < cnt-1; i++)
	{ 
		for(j = cnt-1; 1 + i < j; j--)
		{
			for(k = 0; *(*((pp+j)+k)) == *(*((pp+(j-1))+k)); k++)
			{
				if(*(*((pp+j)+k)) < *(*((pp+(j-1))+k)))
				{
					*p = *(*(pp+j));
					*(*(pp+j)) = *(*(pp+(j-1)));
					*(*(pp+(j-1))) = *p;
				}
			}
		}
	}
}
少し書き直してみたのですが、やはりソートができません。([ ]で記述したときはきちんと実行できたということはこの書き方が間違って・・・;)
・・・strcmpを使って文字列を比較するというのはこのプログラムの場合どうすればいいのでしょうか?
strcmpを使えば文字列比較に便利だと本に書いてあったのですがよく分からなくて・・・。

それとmainの中でchangeを使うときに先頭アドレスを渡すとはどういうことでしょうか?

質問ばかりでごめんなさい;
どこをどういじればいいのか段々わからなくなっておりますが。
ポインタやソートについての分かりやすい初心者向けの本でオススメがあればどなたか教えてください(>_<)

フリオ

Re:コマンドラインから入力するプログラムについて質問です

#10

投稿記事 by フリオ » 17年前

 
 小文字を大文字に変換するための、"toupper"という関数があります。


> [ ]で記述したときはきちんと実行できたということはこの書き方が間違って・・・
 [/url]演算子の式を*演算子の式に変換する場合は、
p -> *(p + i)
です。
また、それ以外は変える必要はありません。


 "sort"関数の文字列を比較する処理は、
同じ文字列が渡されたらどうなるか、考える必要があります。


> strcmpを使って文字列を比較するというのは
> このプログラムの場合どうすればいいのでしょうか?
 "strcmp"は、文字列を文字コード順で比較する関数なので、
文字列を比較している処理の部分を"strcmp"に置き換えてやればいいだけです。

やそ

Re:コマンドラインから入力するプログラムについて質問です

#11

投稿記事 by やそ » 17年前

ポインタについての本

「C言語のポインタがゼッタイにわかる本」・・・ 秀和システム

etc

ぽん

Re:コマンドラインから入力するプログラムについて質問です

#12

投稿記事 by ぽん » 17年前

ありがとうございます!
無事にプログラムを完成させることができました<(_ _)>

> 小文字を大文字に変換するための、"toupper"という関数があります。

こんな便利な関数があるんですね!
さっそく調べて使ってみました。

ポインタの本も本屋に行って探してみます。

長々とありがとうございました。
またお世話になると思いますが、そのときは宜しくお願いします。

閉鎖

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