C言語関係掲示板

過去ログ

No834 strdupの自作

[戻る] [ホームページ]
No.10575

辞書順に並べ替え
投稿者---senshou(2003/11/17 02:09:27)


以前、標準入力で1行ずつ読みこみ、辞書順に並べ替え、1行ずつ標準出力する
というプログラムをここで書いてもらったのですが、strdupという部分がよくわかりません。
レポートでもうまく説明できる自信がありません。
このstrdupがないものは作れないでしょうか?

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

#define MAXLINES 100
#define MAXLENS  100

int cmp(const void *s1, const void *s2)
{
  return strcmp(*(const char **)s1, *(const char **)s2);
}

int main(void)
{
  char *str[MAXLINES];
  char instr[MAXLENS];
  int i = 0, j;
  
  
  while (fgets(instr, MAXLENS, stdin)) {
    str[i] = strdup(instr);
    str[i][strlen(str[i]) - 1] = '\0';
    i++;
  }
  
  qsort((void *)str, i, sizeof(char *), cmp);
  
  for (j = 0; j < i; j++) {
    puts(str[j]);
    free(str[j]);
  }
  
  return 0;
}




No.10576

Re:辞書順に並べ替え
投稿者---northwind(2003/11/17 02:13:44)


質問する前に『strdup』と検索をかけましたか?

No.10578

Re:辞書順に並べ替え
投稿者---senshou(2003/11/17 03:44:07)


>質問する前に『strdup』と検索をかけましたか?

はい。一応検索をし、持っている本を見たところ
「引数によって与えられた文字列を、mallocを呼び出してえられた安全な場所へコピーするもの」
とのことでした。
なんとなくぼやーっとしかわからず、標準関数には入っていないとのこと。
C言語辞典で調べて載っていなくてあせりました。

そんなこともあって、学校の課題ですし、出来ればないほうがいいかな?と。
よろしくお願いします。


No.10581

Re:辞書順に並べ替え
投稿者---northwind(2003/11/17 05:11:41)


> 「引数によって与えられた文字列を、mallocを呼び出してえられた安全な場所へコピーするもの」

つまるところ、文字列を複製するということです( string duplicate )。


> なんとなくぼやーっとしかわからず、標準関数には入っていないとのこと。
> C言語辞典で調べて載っていなくてあせりました。
> そんなこともあって、学校の課題ですし、出来ればないほうがいいかな?と。
> よろしくお願いします。

標準関数として無ければ自作すれば良いだけのことですよね。
下にソースを書いておくので、参考にして下さい。

char *strdup(const char *s)
{
    char *p;

    p = malloc(strlen(s) + 1);  /* 文字列終端子の分、一つ多く領域確保 */
    if (p != NULL)              /* メモリ確保に成功した場合 */
        strcpy(p, s);           /* 文字列をコピーする */

    return p;
}

ちなみに、strdup() で複製している文字列の領域は内部で malloc() を使って確保しているので、
使い終わった時に free() で解放しなくてはなりません。
その為、あなたが示したソースは最後辺りで free() していますね。
そういった所も説明出来るようにしておけば良いと思います。


最後に、
> このstrdupがないものは作れないでしょうか?
という問いには、私には答えられそうにありません。
これが最も普通のやり方ではないでしょうか。

他に分からない所があって、考えても分からなければ聞いて下さい。
私も考えます。

No.10616

Re:辞書順に並べ替え
投稿者---senshou(2003/11/18 01:23:32)


while (fgets(instr, MAXLENS, stdin)) {
    str[i] = strdup(instr);
    str[i][strlen(str[i]) - 1] = '\0';
    i++;
  }


この部分の確認と質問をしたいのですが、
1行目の条件の部分はinstrに読みこめるだけ読みこむ
2行目はstrにinstrをコピー
3行目は文字列の最後に\0をつけている
ということでいいのでしょうか?

No.10618

Re:辞書順に並べ替え
投稿者---senshou(2003/11/18 01:40:04)


何度もすみません・・・

もうひとつ質問したいのですが、
よく「getsよりfgetsのほうがいい」と聞くのですけど、
それはなぜでしょうか?

No.10622

Re:辞書順に並べ替え
投稿者---YuO(2003/11/18 02:30:54)


まとめて答えてしまいます。


>1行目の条件の部分はinstrに読みこめるだけ読みこむ

fgetsの動作を勉強して下さい。

最大(MAXLENS - 1)文字を,stdinから改行を見つけるまでinstrに読み込む,という動作になります。


>3行目は文字列の最後に\0をつけている

文字列の最後にある(であろう)'\n'を'\0'に置換しています。


ライブラリ関数の動作とコードの動作を,ちゃんと追っていかないと実際の動作は辿ることができませんよ。


>もうひとつ質問したいのですが、
>よく「getsよりfgetsのほうがいい」と聞くのですけど、
>それはなぜでしょうか?

getsはbuffer overrunを見逃します。

FAQですので,ちょっと探すとちゃんとみつかります。
標準入出力関数(2) : gets()
過去ログ No.70 gets()は使わないほうがいい
C FAQ 7.23


No.10623

Re:辞書順に並べ替え
投稿者---northwind(2003/11/18 02:31:03)


> よく「getsよりfgetsのほうがいい」と聞くのですけど、
> それはなぜでしょうか?

gets() では入力最大長を指定出来ませんが、
fgets() ではそれが出来ます。
それが、どう違ってくるのかを下にサンプルとして書きます。参考にして下さい。
----------
char str[80];

gets(str);                /* 80 文字以上入力されると配列を超えて書き込まれてしまう */
fgets(str, 80, stdin);    /* 80 文字以上入力されても、途中で打ち切られ安全 */
----------


No.10621

Re:辞書順に並べ替え
投稿者---northwind(2003/11/18 02:20:42)


> この部分の確認と質問をしたいのですが、
> 1行目の条件の部分はinstrに読みこめるだけ読みこむ

標準入力( stdin )から instr に一行読み込み、
EOF の出現か エラーが発生した場合はループを終了させる、というのが正確ですね。

(MAXLENS というのは『行の最大長』という意味で定義しているので、
 実際は MAXLENS より長い行が読み込まれた場合は、
 途中で読み込みを打ち切られこちらの意図しない動作をしますが、
 あくまで『行の最大長』と定義していることに注意して下さい)

> 2行目はstrにinstrをコピー

惜しいです。コピーではなくて str[i] に複製しています。
実際には、登録すると言った方が概念としては分かりやすいでしょう。

> 3行目は文字列の最後に\0をつけている

そうです。文字列終端子をつけています。

No.10624

訂正
投稿者---northwind(2003/11/18 02:41:10)


>> 3行目は文字列の最後に\0をつけている
> そうです。文字列終端子をつけています。

ごめんなさい、全然違ってました。
正しくは、YuO さんが No.10622 で仰っていることを参照して下さい。

No.10625

Re:辞書順に並べ替え
投稿者---senshou(2003/11/18 03:17:25)


わかりやすい解説ありがとうございます。
さきほどのコードに自分なりにコメントをつけてみました。
追加、訂正するべきところなどありましたら、
どんどんご指摘のほうよろしくおねがいします。

それと「コピー」と「複製」の違いとはなんでしょうか?


#include <stdio.h>
#include <stdlib.h>		/* qsort()を使うのに必要 */
#include <string.h>		/* strcmp()を使うのに必要 */

#define MAXLINES 256	/* 最大行 */
#define MAXLENS  256	/* 最大文字列長 */

int cmp(const void *s1, const void *s2)		/* qsort()で使う */
{
	return strcmp(*(const char **)s1, *(const char **)s2);	/* s1とs2の比較 */
}

char *strdup(const char *s)
{
	char *p;

	p = malloc(strlen(s) + 1);	     /* 文字列終端子の分、一つ多く領域確保 */
	if (p != NULL) strcpy(p, s);	/* メモリ確保に成功した場合、文字列をコピーする */

	return p;
}

int main(void)
{
	char *str[MAXLINES];	/* 文字列を記憶するための配列 */
	char instr[MAXLENS];	/* 文字列を「一時的に」記憶するための配列 */
	int i = 0, j;
    
	while (fgets(instr, MAXLENS, stdin)) {	    /* 標準入力( stdin )から instr に一行読み込み、ファイルの終わりまでかエラーが発生した場合はループを終了させる */
		str[i] = strdup(instr);		    /* str[i]にinstrを複製 */
		str[i][strlen(str[i]) - 1] = '\0';    /* 文字列の最後にある'\n'を'\0'に置換 */
		i++;		     /* iを進め、行を格納するstr[i]を進める */
  }
  
  qsort((void *)str, i, sizeof(char *), cmp);	/* ソート */
  
  for (j = 0; j < i; j++) {
	puts(str[j]);		/* 今までstrに格納し、ソートされたものを出力 */
	free(str[j]);		/* strdup()で確保した文字列の領域の開放 */
  }
  
  return 0;
}




No.10627

Re:辞書順に並べ替え
投稿者---northwind(2003/11/18 04:19:39)


最初に断っておきますが私はあなたの講師ではなくて、
ただのボランティア(それも間違って説明してしまうような)です。
それと、YuO さんのレスを熟読なされた方が良いと思います。

> それと「コピー」と「複製」の違いとはなんでしょうか?

普通は同じ意味なのでしょうが、ここではわざわざ違う意味として使っています。

コピー:文字列を複写する( strcpy() )
複製 :領域を確保してクローンを作成する( strdup() )

私の国語力が足りず分かりにくいかもしれませんが、
ただ単純にコピーするだけなのか、同一のオブジェクトを作成するのか、
という違いで使い分けています。

例を挙げるならば、
授業ノートを二冊貸してもらって内容だけを書き写すのか、
一冊だけを貸してもらい、もう一冊は新たに購入して書き写すのか、
ということです。

> #define MAXLENS 256/* 最大文字列長 */

MAXLENS というのは詳しく書けば『行の最大文字列長』です。
よって MAXLENS より、MAX_LINE_OF_LEN, MAX_LINELEN 等の方が
断然分かりやすいでしょうね。

あと、式のコメント以外にも関数のコメントも必要だと思います。
簡単に書けばこんな感じになります。
/* 文字列を複製し、複製した文字列の先頭を指すポインタを返す */
char *strdup(const char *s)
{
    ...;
}

これに、引数や戻り値は何を意味するのか、この関数を使うに当たっての注意点は何なのか、
等ということを簡潔に書いておけばバッチリではないでしょうか。

No.10628

ありがとうございました
投稿者---senshou(2003/11/18 04:36:29)


長々と説明させてしまって、本当に申し訳ありません。

YuO さん、northwind さん本当にありがとうございました