ポインタと文字列

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

ポインタと文字列

#1

投稿記事 by beginner » 14年前

ポインタを用いて文字列を入力してその文字列を表示させる(配列を使ってはいけない)というプログラムをつくっているのですが、
文字列を入力しているときに上書きされてしまって一番最後に入力した文字しか表示されません。
入力した文字列をすべて表示させるにはどのようにするべきなのでしょうか?

コード:

#include<stdio.h>
int main(void) {
	char *a;
	int i,n;
	printf("文字列の長さを入力してください\n");
	scanf("%d",&n);
	printf("文字列を入力してください\n");
	for(i=0;i<n;++i)
		scanf("%s",&a);
	for(i=0;i<n;++i)
	printf("%c\n",a);
}

アバター
h2so5
副管理人
記事: 2212
登録日時: 14年前
住所: 東京
連絡を取る:

Re: ポインタと文字列

#2

投稿記事 by h2so5 » 14年前

ヒント:ポインタには++演算子が使えます。
ポインタの指すアドレスを一つ進めることができます。

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: ポインタと文字列

#3

投稿記事 by bitter_fox » 14年前

beginner さんが書きました:ポインタを用いて文字列を入力してその文字列を表示させる(配列を使ってはいけない)というプログラムをつくっているのですが、
文字列を入力しているときに上書きされてしまって一番最後に入力した文字しか表示されません。
入力した文字列をすべて表示させるにはどのようにするべきなのでしょうか?

コード:

#include<stdio.h>
int main(void) {
	char *a;
	int i,n;
	printf("文字列の長さを入力してください\n");
	scanf("%d",&n);
	printf("文字列を入力してください\n");
	for(i=0;i<n;++i)
		scanf("%s",&a);
	for(i=0;i<n;++i)
	printf("%c\n",a);
}
まず、本題の前に、aが確保されてないのに関わらず、文字列を代入しようとしています。

出力も、n回、ポインタaの値(ここは一般にアドレスが入ってる)を文字として出力している。

もし、やるのであれば、ダブルポインタにして動的に確保するのが妥当な方法でしょう。

PS:「文字列の長さ」というよりかは、現在のプログラムでは、「文字列の個数」とした方が適切かと思います。

[hr][追記]
んん、入力される文字列は一つなのかな・・・?
今のプログラムでは、文字列を複数個入力されるのか、文字を複数個入力されて一つの文字列になるのかがはっきりしてないのですが・・・
どちらでしょうか??

beginner

Re: ポインタと文字列

#4

投稿記事 by beginner » 14年前

h2so5さん,bitter_foxさん,コメントありがとうございます。
>>bitter_foxさん
入力する文字列は一つです。
h2so5さんにもお願いしたいことなのですが、もう少し詳しくヒントを出してもらいたいです。

box
記事: 2002
登録日時: 14年前

Re: ポインタと文字列

#5

投稿記事 by box » 14年前

>もう少し詳しくヒントを出してもらいたいです。

「本来は」自分の脳みそをフル回転させてプログラムを完成させるのが筋である、ということはおわかりですか?
あまり詳しくヒントを出し過ぎると、答えそのものを書いてしまうことにもなりかねず、
それでは質問者さんのためにならない、という判断が働いているのではないでしょうか。
与えられたヒントを頼りに、「自分の頭や手を使って(自分で調べたり本を読んだりして)」、
完成まで持っていってみてはどうでしょう。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

アバター
h2so5
副管理人
記事: 2212
登録日時: 14年前
住所: 東京
連絡を取る:

Re: ポインタと文字列

#6

投稿記事 by h2so5 » 14年前

>もう少し詳しくヒントを出してもらいたいです。

bitter_foxさんの仰るような、プログラムの本題以前の問題が有ったのに、
そこを指摘せずにヒントを書いてしまったので、分かりにくかったかもしれません。
今度からはもう少し冷静にコードを見るようにします。申し訳ありません。

また、私の出したヒントはあくまで「配列を使わずに文字列を保持するためのポイント」を言っているだけで、
beginnerさんのコードにそれを適用すれば即動作する訳ではないことに注意してください。
(検索キーワードのような物です(´・ω・`))
最後に編集したユーザー h2so5 on 2011年1月19日(水) 23:41 [ 編集 1 回目 ]

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: ポインタと文字列

#7

投稿記事 by bitter_fox » 14年前

beginner さんが書きました: h2so5さんにもお願いしたいことなのですが、もう少し詳しくヒントを出してもらいたいです。
では、動的確保のやり方はご存知でしょうか??

また、普通にchar型の配列として宣言されているhogeに対して、文字列を入力してもらうプログラムを書くことは可能ですか??(配列は十分な大きさで確保されているとする。)

次に、入力してもらったhogeを出力することは可能ですか??

あと、質問文のプログラムでは、入力と出力のアルゴリズムがそれぞれ違ってますが、何らかの指示や意図があるのでしょうか??

beginner

Re: ポインタと文字列

#8

投稿記事 by beginner » 14年前

>>h2so5さん
分かりました。ありがとうございます。

>>bitter_foxさん
動的確保のやり方が全く分からないです。
出力と入力のアルゴリズムが違うという点なんですが、特に指示や意図といったものはありません。
コードを以下のように書き直してみたのですが、まだ表示させる段階で完璧ではありません。出力させると空欄の部分がでてきてしまいます。
そのような点を改善するにはどのようにすればよろしいですか?

コード:

#include<stdio.h>
int main(void) {
	char *a,**b;
	int i,n;
	printf("文字列の個数を入力してください\n");
	scanf("%d",&n);
	printf("文字列を入力してください\n");
	for(i=0;i<n;++i) {
		scanf("%c",&a);
		b=&a;
	printf("%c",b);
	}
	printf("\n");
	return 0;
}

アバター
h2so5
副管理人
記事: 2212
登録日時: 14年前
住所: 東京
連絡を取る:

Re: ポインタと文字列

#9

投稿記事 by h2so5 » 14年前

まずはっきりさせたいのは、
文字列の入出力を一度にするのか、それとも1文字ずつにするのか、ということです。

beginnerさんのコードは2つが混ざっている気がします。
また、文字列を一度に入出力する方法ですと、私がヒントで言った++演算子は必要ありません。

動的確保はmallocで検索してみてください。

beginner

Re: ポインタと文字列

#10

投稿記事 by beginner » 14年前

>>h2so5さん
文字列の入出力を1文字ずつでやりたいです。
mallocで検索してじっくりとは調べていないのですが、あまりよい関数ではないとの表記がありました。
使っても大丈夫な関数なのでしょうか?

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: ポインタと文字列

#11

投稿記事 by bitter_fox » 14年前

beginner さんが書きました: コードを以下のように書き直してみたのですが、まだ表示させる段階で完璧ではありません。出力させると空欄の部分がでてきてしまいます。
そのような点を改善するにはどのようにすればよろしいですか?

コード:

#include<stdio.h>
int main(void) {
	char *a,**b;
	int i,n;
	printf("文字列の個数を入力してください\n");
	scanf("%d",&n);
	printf("文字列を入力してください\n");
	for(i=0;i<n;++i) {
		scanf("%c",&a);
		b=&a;
	printf("%c",b);
	}
	printf("\n");
	return 0;
}
これだと、確保されていないaに入力してもらって、
bにaのアドレスを代入して。
そのアドレスの値を文字として出力しようとしてますね。
(しかも、これだと、文字列を入力してもらってその文字列を出力するというより、文字を入力してもらってそれを反復しているといった感じになってます。)

配列を使ったバージョンを書いていただけますでしょうか??
bitter_fox さんが書きました: また、普通にchar型の配列として宣言されているhogeに対して、文字列を入力してもらうプログラムを書くことは可能ですか??(配列は十分な大きさで確保されているとする。)

次に、入力してもらったhogeを出力することは可能ですか??
これに対する返答もお願いします。

アバター
h2so5
副管理人
記事: 2212
登録日時: 14年前
住所: 東京
連絡を取る:

Re: ポインタと文字列

#12

投稿記事 by h2so5 » 14年前

beginner さんが書きました: 使っても大丈夫な関数なのでしょうか?
確かにむやみにmallocを使うべきではありませんが、
配列を使ってはいけないという条件付きですので、mallocを使うしかないでしょう。

そして、文字が上書きされないようにするためには、
入力されるたびに代入する場所を変える(ずらす)必要があります。そこで++を使います。

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: ポインタと文字列

#13

投稿記事 by bitter_fox » 14年前

beginner さんが書きました:>>h2so5さん
文字列の入出力を1文字ずつでやりたいです。
mallocで検索してじっくりとは調べていないのですが、あまりよい関数ではないとの表記がありました。
使っても大丈夫な関数なのでしょうか?
取りあえず、一度じっくりと調べてみてください。

また、「あまりよい関数ではない」とのことですが、決してそんなことはないです。
むしろ、この関数がないとダイナミックなプログラミングができないです。

beginner

Re: ポインタと文字列

#14

投稿記事 by beginner » 14年前

>>bitter_foxさん
配列を用いたコードを書きました。

コード:

#include<stdio.h>
int main(void) {
	char hoge[100];
	int i,n;
	printf("文字列の個数を入力してください\n");
	scanf("%d",&n);
	printf("文字列を入力してください\n");
	for(i=0;i<n;++i)
		scanf("%s",&hoge[i]);
	for(i=0;i<n;++i)
		printf("%c\n",hoge[i]);
	return 0;
}

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: ポインタと文字列

#15

投稿記事 by bitter_fox » 14年前

beginner さんが書きました:>>bitter_foxさん
配列を用いたコードを書きました。

コード:

	for(i=0;i<n;++i)
		scanf("%s",&hoge[i]);
	for(i=0;i<n;++i)
		printf("%c\n",hoge[i]);
ありがとうございます。

非常に惜しいですね、一文字ずつ入力されるんですよね??
であれば、scanf関数の第一引数は"%s"でよいのでしょうか??

beginner

Re: ポインタと文字列

#16

投稿記事 by beginner » 14年前

<<bitter_foxさん
一文字ずつ入力するときは"%S"ではなく"%C"です。

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: ポインタと文字列

#17

投稿記事 by bitter_fox » 14年前

beginner さんが書きました:<<bitter_foxさん
一文字ずつ入力するときは"%S"ではなく"%C"です。
そうです、"%c"です。

では、次に、さっきのプログラムを動的確保したものに変更したいのですが、まず動的確保の説明をします。

動的確保というのは、メモリを動的に確保するというテクニックです。(まんまですね。)
動的確保されるメモリは、ヒープ領域という領域に割り当てられてるメモリで、これらのメモリはOSが管理しています。
ですので、先ほども出た、malloc関数などでOSにメモリを確保してもらう必要があります。

では、その手順は、

コード:

①メモリを動的に確保する関数を呼び出す。

②メモリ確保に成功したかを調べる。

③プログラマがそのメモリを使う。

④確保したメモリを解放する。
の4つになります。

特に大事なのは、①と②と④です。

①のメモリを動的に確保する関数は、
malloc, calloc, realloc
主にこの三つが使われます。(reallocが確保の際に使われるのは稀)
また、これらの関数を使うためには、stdlib.hをインクルードする必要があります。
これらの関数の使い方は、
http://msdn.microsoft.com/ja-jp/library/6ewkz86d.aspx
http://msdn.microsoft.com/ja-jp/library/3f8w183e.aspx
http://msdn.microsoft.com/ja-jp/library/xbebcx7d.aspx

ここを読むと、「戻り値に型キャストを行います」とあります。
これは、これらの関数の戻り値の型はvoid*型のため、各々の型に変換してあげる必要があるのです。(と言ってもC言語ではvoid*は自動的に型キャストされるのでしなくても問題はありませんが、C++では明示的にキャストしないとコンパイルエラーになってしまいます。)

次に、②のメモリ確保に成功したかを調べるですが、
あれら、3つの関数は、メモリ確保に失敗するとNULLポインタを返します。
ですので、

コード:

if (p == NULL)
{
	/* エラー処理 */
}
といった風にする必要があります。

そして、③の確保したメモリを開放するは、
free関数を使います。
この関数の使い方は、
http://msdn.microsoft.com/ja-jp/library/we1whae7.aspx
を参考にしてください。
この関数で解放した後、解放したアドレスにアクセスしたりもう一度freeしたりすると、エラーが発生する可能性があるので注意してください。

これらのURL先の方が多分わかりやすいです・・・w
[参考URL]
http://homepage2.nifty.com/c_lang/intro/no_33.htm
http://homepage3.nifty.com/mmgames/c_gu ... alloc.html
http://wisdom.sakura.ne.jp/programming/c/c55.html

beginner

Re: ポインタと文字列

#18

投稿記事 by beginner » 14年前

>>bitter_foxさん
大変丁寧に説明していただき分かりやすかったのですが、
malloc関数はあまり使わない方がよいと書いてあるサイトもあったのですが大丈夫なのでしょうか?

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: ポインタと文字列

#19

投稿記事 by bitter_fox » 14年前

beginner さんが書きました: malloc関数はあまり使わない方がよいと書いてあるサイトもあったのですが大丈夫なのでしょうか?
大丈夫です、おそらくそのサイトは、メモリリークとか解放したメモリにアクセスした際のエラーなどのこと(メモリ管理の煩わしさ)を考慮してのことでしょうが、これらの点は、あくまでプログラマの責任なので、malloc関数がどうとかそういう話ではないです。

また、あまり使わない方がよいというのは、動的確保に対してなのかもしれませんが、このプログラムでは、動的確保を行うようにという指示が出てるのでその点を心配するのは不毛かと思います。

PS:この件に関しては、h2so5さんもすでに返答されていますが、ご確認されましたでしょうか??

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

Re: ポインタと文字列

#20

投稿記事 by maru » 14年前

beginner さんが書きました:>>bitter_foxさん
大変丁寧に説明していただき分かりやすかったのですが、
malloc関数はあまり使わない方がよいと書いてあるサイトもあったのですが大丈夫なのでしょうか?
bitter_foxさんが説明されている通りmalloc関数を使用すること自体には問題がほとんどありません。
多用することによってメモリの断片化が発生する可能性がありますが、初心者が気にするような問題ではありません。
参考のため、そのサイトのURLを教えていただけますか?

beginner

Re: ポインタと文字列

#21

投稿記事 by beginner » 14年前

>>bitter_foxさん
同じことをきいてしまっていたようです。すみません。

>>maruさん
サイトのURLです。
http://ja.wikipedia.org/wiki/Malloc

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: ポインタと文字列

#22

投稿記事 by softya(ソフト屋) » 14年前

引用:wikipediaより。 http://ja.wikipedia.org/wiki/Malloc
C++でもmalloc関数は利用できるが、この利用は後述の問題を引き起こすため推奨されない。C++では言語の機能としてnew演算子、delete演算子が用意されている。mallocで確保したメモリ領域に対してdeleteしたり、逆にnewで確保した領域をfreeしたりすると結果は未定義となる。mallocによって生まれたポインタとnewによって生まれたポインタの混在はバグの温床であり、また、new/delete演算子と違い、malloc/free関数ではクラスのコンストラクタとデストラクタが呼ばれないという違いもあり、C++でのmallocは禁じ手の扱いである。
問題は、この文でしょうか?
これは、C++について書かれた事ですのでC言語では関係ないです。

mallocが動的確保のためバグの危険を伴うことは間違いないですが、ポインタを使っている時点でバグの危険はあります。
配列を使わず、ポインタで使用するメモリを得る方法は、C言語標準関数の範囲ではmallocでメモリ確保するしかありません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

beginner

Re: ポインタと文字列

#23

投稿記事 by beginner » 14年前

>>softya(ソフト屋)さん
その文章で間違いないです。
そうだったんですか。自分の読み間違いだったようですね。

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: ポインタと文字列

#24

投稿記事 by bitter_fox » 14年前

ところで、ここへの返信や
[参考URL]
http://homepage2.nifty.com/c_lang/intro/no_33.htm
http://homepage3.nifty.com/mmgames/c_gu ... alloc.html
http://wisdom.sakura.ne.jp/programming/c/c55.html
などを読んで動的確保の仕方は、理解されましたでしょうか??

閉鎖

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