• ベストアンサー

文字列ポインタとgets関数の関係について。

以下のプログラムはコンパイルは出来ますが、 実行するとクラッシュしてしまいます。 gets関数は char *gets( char *str ); と定義されているので文字列の先頭アドレスを返すはずですが 何故このプログラムはエラーが出るのでしょうか・・。 #include <stdio.h> int main ( void ){  char *p, *s;  p = gets(s);  printf("%s", p);  return 0; }

質問者が選んだベストアンサー

  • ベストアンサー
  • ham_kamo
  • ベストアンサー率55% (659/1197)
回答No.5

No.1の者です。お礼欄拝見しました。 まずpはこの際関係ありません。問題はあくまでもgets(s)にあります。 参考URLから引用しますが、 char *gets(char *s); gets() は、改行文字か EOF までの 1行を stdin から読み込み s が指すバッファに格納する (末尾の改行文字や EOF は '\0' に置き換えられる)。バッファ・オーバーランのチェックは行われない (下記の バグ を参照)。 という動作をします。つまり、gets(s)を実行することによって、引数で指定したポインタ変数sが指示するメモリに書き込みが行われます。このとき、ご質問のプログラムのようにsが正しく確保されたメモリ領域をへのポインタを保持していないと、領域破壊が行われます。 sに正当に確保されたメモリへのポインタが代入されているのであれば、 p=s; または p=gets(s); とするのは、単にpもsと同じメモリ領域を指すことになるので、何も問題ありません。 なお、他の回答者も書いておられますが、C言語ではこの辺のメモリ管理についてよく理解し、プログラム内で厳密にメモリの管理を行う必要があります。そうでないと、バッファを破壊したりメモリリークを起こしたりするので注意が必要です。(他の言語のようにガベージコレクションなどは勝手にやってくれません) No.2やNo.3の方が書いておられますが、NULLチェックを必ず行うとか、gets()のような不備のある関数は使わない、malloc()で動的に確保したメモリは確実にfree()するなどの注意が必要です。やっかいなところではありますが、Cで開発を行う以上避けて通れない道なので、これからメモリ管理を意識して勉強されるといいと思います。

参考URL:
http://www.linux.or.jp/JM/html/LDP_man-pages/man3/fgets.3.html

その他の回答 (6)

  • bnosuke-x
  • ベストアンサー率39% (43/110)
回答No.7

すみません。訂正です。 誤 mfree(s) 正 free(s) でした。 これだけではなんなので... ググるときはたとえば malloc free メモリーリークなどとして探してください。

  • bnosuke-x
  • ベストアンサー率39% (43/110)
回答No.6

びーのすけと申します。 皆さんの連係プレーでいいお話が出てきたので僭越ながらワタクシもちょっと補足させていただきます。 メモリを意識するときの注意点として、メモリの確保と同じくらい(またはそれ以上に)重要なものとして、「メモリの解放」があります。 C言語はメモリの確保と解放はプログラマがきちんと目を見張らせないととんでもないことになります。 今回の場合、#1さんのプログラムを例にとらせていただきますと、 s[256] の場合は問題はないです。 ですが、 s=malloc(256); とした場合は、s に用が無くなったら mfree(s); としてシステムにメモリを帰す必要があります。 メカニズムの詳細は質問から大きく離れてしまうのでここでは割愛しますが、これを守らないと、1回や2回実行しただけでは不具合の出ない、しかも一見して見つけにくい悪魔のようなバグができてしまいます。 メモリ周りの勉強のキーワードとして、頭に留め置いてください。

  • galluda
  • ベストアンサー率35% (440/1242)
回答No.4

がると申します。 他の方がとてもきれいに一通り説明なさっているので、簡易的補足をちょろりと。 C言語は、物凄く「メモリを意識する必要がある」言語です。 そのために、基本的にすべての「データを入れる変数の領域の確保」を意識する必要があります。 特にchar *とかの場合において非常に重要で初心者のかたが分からなくなりがちなので。 そういった「メモリ周り」とかをきちんと学ぶか、教わるか、するとよろしいかと思われます。

回答No.3

余談ですが、gets()は入力の制限ができない大変危険な関数なので、安全に使えるfgets()をお勧めします。 #include <stdio.h> int main() { char *p; char s[200]; p = fgets(s, sizeof(s), stdin); if (p != NULL) { printf("%s", s); } return 0; }

amazontester
質問者

お礼

fgetsってこういう関数だったんですね。 勉強になりました。ありがとうございます。 これからは極力こっちを使うようにしたいです。

  • m_mik
  • ベストアンサー率26% (31/117)
回答No.2

#1の方がお答えになっているように、領域を確保することで解決しますが、ついでに気になったことを… printf("%s" , p); とされていますが、getsで入力がなかった場合には p がNULLになってしまいます。 getsの使い方としては、sのバッファに入力データを格納する。 pは正しく入力されたかどうかを確認するために使用するのが良いと思います。 p = gets(s) if (p != NULL) { printf("%s" , s); } return 0;

amazontester
質問者

お礼

なるほど。ヌルポインターを忘れていました。 ありがとうございます。

  • ham_kamo
  • ベストアンサー率55% (659/1197)
回答No.1

このソースでは、sはポインタとして宣言されているだけで、実際にgetsで取得した文字列を格納するメモリ領域が確保されてません。 char *p,s[256]; のように配列として宣言するか、 char *p,*s; s=malloc(256); のように動的にメモリ領域を確保する必要があります。

amazontester
質問者

お礼

なるほど。最初に領域が決まっていないので領域破壊が起こるということですね。 ところで以下のプログラムはちゃんと動くのですが、これの場合 p = gets(s); を gets(s); p = s; と書き換えただけです。 getsの戻り値がchar *型なのでそれがsに代入されてさらにその値をpに代入しているわけですが、 p = gets(s);とするのだってpにchar *型の戻り値を代入しているだけなのに何故領域が破壊されるのか分かりません。 両者の違いとは何なのでしょうか? p = sなどとしたときは p に s 分の領域が確保されるのでしょうか? #include <stdio.h> int main ( void ){  char *p, *s;  gets(s);  p = s;  printf("%s", p);  return 0; }

関連するQ&A