• 締切済み

このソースの意味を教えてください

構造体を使ったリストを理解するためのソースで http://www9.plala.or.jp/sgwr-t/c/sec15-5.html のページに以下の関数が書いてありました。 自分はreturn p;とするのが自然であって、head = p;の意味が分かりません。それが不要な処理であるように思えます。 return head;としているのでその前にhead = p;は必要な処理ですが、無駄であって意味が無いように見えましたが、そのように書くのが自然なのでしょうか? head = p;の右に書いてあるコメントもおかしいと思うのですが、皆さんにはそのページのソースのその部分はコメントも含めて不自然さを感じないでしょうか? /*** リストにデータを登録 ***/ struct list *add_list(int key, char *name, struct list *head) {   struct list *p;   /* 記憶領域の確保 */   if ((p = (struct list *) malloc(sizeof(struct list))) == NULL) {     printf("malloc error\n");     exit(EXIT_FAILURE);   }   /* リストにデータを登録 */   p->key = key;   strcpy(p->name, name);   /* ポインタのつなぎ換え */   p->next = head;  /* 今までの先頭ポインタを次ポインタに */   head = p;     /* 新たな領域を先頭ポインタに */   return head; }

みんなの回答

  • Lchan0211
  • ベストアンサー率64% (239/371)
回答No.10

回答1、回答2は勘違いされているので無視していいとして、 私もadd_list()に必要な機能としては、確かに無駄な処理だと思います。 が、「トップキューイン処理」の定石コーディングとしては、 p->next = head; head = p; の2行をセットで記述するものと考えた方がよいと思いますよ。 例えば、今のadd_list()を拡張して、1度に複数個のリストを追加するような 関数を作りたくなった場合、今のコーディングなら、 malloc行~head=p行までをfor文で囲むコーディングを 追加するだけで済みます。 リスト処理の定石に従ってコーディングしていれば、このような拡張に 対応しやすくなります。 解説サイトにあるような片方向リストは、まだ簡単なのですが、 双方向リスト等の複雑なリスト構造になると、 処理すべきポインタが増えるので、 ・トップキューイン処理 ・ラストキューイン処理 ・中間挿入処理 ・リスト削除処理 といったそれぞれのリスト処理を定石パターン化すると、 ミスを減らすことができます。 一見、無意味な勘違いコーディングをしているようにも見えますが、 ミスのないコーディングをするために、多少無駄でも 定石パターンを大事にしてコーディングすることは、 とても有意義なことだったりします。

全文を見る
すると、全ての回答が全文表示されます。
  • S117
  • ベストアンサー率40% (18/45)
回答No.9

まず前提としてこのソースは「リストの説明をするためのソース」です。 で、問題の部分 p->next = head;  /* 今までの先頭ポインタを次ポインタに */ head = p;     /* 新たな領域を先頭ポインタに */ は強調表現がされています。 つまり、リストへの要素追加は  今までの先頭ポインタを次ポインタに して  新たな領域を先頭ポインタに する という説明がしたかったのでしょう。 この説明を関数がまたがる形で表現したくなかったために、わざわざこの位置に head = p; を記述したと考えられます。 しかしご指摘の通り、add_listのローカル変数headに代入するのは重要ではなく、本当の挿入操作が終わるのは、mainのローカル変数headを書き換えた時点。つまり、add_listが戻った時点なので、あまりいい説明ではありません。 ではどうあるべきなのでしょうか。 add_listの代わりに、 新しいlist構造体を確保し、初期化してそのポインタを返す関数、 struct list *new_list(int key, char *name) を用意し、 呼び出し元(main)で、 /* リストにデータを登録 */ tmp = new_list(key, name); tmp->next = head;  /* 今までの先頭ポインタを次ポインタに */ head = tmp;     /* 新たな領域を先頭ポインタに */ としておけば、要素の挿入操作を一カ所で書けて、さらに余計な代入操作を避けられます。

全文を見る
すると、全ての回答が全文表示されます。
  • rabbit_cat
  • ベストアンサー率40% (829/2062)
回答No.8

まあ、return p; でもいいんでしょうが、 実際のところ、最近のコンパイラであれば、ほぼ間違いなく head = p; return head; も、 return p; もコンパイル結果は全く同じだと思われます。 なんで、スタイルというか、思考過程が違うってことで、でいいではないでしょうか。 ・リストの先頭は常にheadで、挿入処理に伴ってheadを更新した、て思えば、質問文のようなコードになるでしょうし、 ・リストの先頭がheadからpに変わった、って思えば、return p; になるでしょう。

全文を見る
すると、全ての回答が全文表示されます。
  • mk48a
  • ベストアンサー率56% (1133/2007)
回答No.7

#3,4です。 >可読性を向上という意味ですか。 >僕には、ソースを書いた人が何か勘違いをしているように見えるんですよ。 ソースを書いた人の意図はソースを書いた人にしかわかりませんので、個人の意見です。 最近は、無駄を省いたプログラムよりも可読性を重視したメンテナンスの容易なプログラムの方が好まれる傾向にあります。 私が作者の記述方法にも一定の利点があると思うのは、ポインタのつなぎ換えの処理の後にリストの先頭に関する操作を追記する必要があった場合に、わかりにくいpではなくheadの変数でアクセスできるので、メンテナンスが容易である点です。(headはリストの先頭なので) 個人で作っているプログラムなら自分で把握できていると思いますが、大規模プログラムを分担してコーディングしているとわかりやすい変数名はバグの抑止にも役立ちます。(なので、pという変数名はわかりづらいので使わない) また、自分で作ったプログラムでも、後から読み返す際に忘れてしまっていることなどよくあります。 まぁ、未熟云々ではなくてコーディングスタイルの問題なので、「この人はこう書くんだ」くらいに思っているのが良いと思います。 苦労して無駄を省いたコードを書いても、コンパイルしてみたら結果は同じだったなんてことは良くありますし。

全文を見る
すると、全ての回答が全文表示されます。
  • Tasuke22
  • ベストアンサー率33% (1799/5383)
回答No.6

> やっぱり僕がまだ未熟でしたね。 いえ、私の方が未熟のようです。 お二方がheadの内容は変わらないと仰っています。 私は思考放棄です(^^;) 失礼しました。

stbsheth
質問者

お礼

あれが不自然に見えるという方が他にいなければ、不自然に見えてしまう僕は やっぱり未熟なんでしょうね。 ありがとうございます。

全文を見る
すると、全ての回答が全文表示されます。
  • zwi
  • ベストアンサー率56% (730/1282)
回答No.5

あっ、struct list **headじゃありませんでしたね。 失礼しました。

全文を見る
すると、全ての回答が全文表示されます。
  • mk48a
  • ベストアンサー率56% (1133/2007)
回答No.4

#3です。追加。 この関数の役割を考えてみましょう。 この関数は、「keyとnameとリストの先頭ポインタを受け取って、新しい構造体を追加し、リストの先頭ポインタを返す関数」です。 引数struct list *headはスタック上にコピーが生成されるので、この関数が終了したらクリアされます。 なので、この関数内でheadをいくら変更してもこの関数の呼び出し元での変化はありません。 呼び出し元ではこの関数の戻り値を新しくリストの先頭として使用します。 個人的な感想としては、リストの先頭ポインタがころころ変わる変数を生で使うのはなんとなく気持ち悪いので、このような書き方はしませんが、リスト構造の処理としては普通の処理です。

stbsheth
質問者

お礼

ありがとうございます。

全文を見る
すると、全ての回答が全文表示されます。
  • mk48a
  • ベストアンサー率56% (1133/2007)
回答No.3

ステップ数を減らすなら、 head=p; の行を削除して、 return p; で良いですが、まぁ、この関数の中ではheadはリストの先頭としているようなので、ポインタをつなぎ変えた後に head=p; とするのは、可読性を向上させる意味ではアリだと思います。 私ならリストを管理する構造体を作ってそのポインタを受け渡ししますが、小さなプログラムならば特に問題は無いと思います。

stbsheth
質問者

お礼

ありがとうございます。 可読性を向上という意味ですか。 僕には、ソースを書いた人が何か勘違いをしているように見えるんですよ。

全文を見る
すると、全ての回答が全文表示されます。
  • zwi
  • ベストアンサー率56% (730/1282)
回答No.2

きわめて合理的なプログラムですよ。   head = p;     /* 新たな領域を先頭ポインタに */ を消して、   return p; だけにしてみたら分かりますがshow_listもfree_listも、まともに動作しなくなります。 これがリスト構造ですので、もう一度ちゃんと調べてみてください。 しかし、疑問に思ったらなぜ試してみないで、この掲示板に質問する人が多いのだろう・・・。試せばすぐ分かるのに。

stbsheth
質問者

補足

ちなみにあなたは実際に   head = p;  /* 新たな領域を先頭ポインタに */   return head; の2行を   return p; にして試してまともに動作しなくなることが確認できたのでしょうか? 僕の方ではまともに動作しているように見えました。

全文を見る
すると、全ての回答が全文表示されます。
  • Tasuke22
  • ベストアンサー率33% (1799/5383)
回答No.1

return head; だから head=p; が必要なのでは ありません。 元々 head=p; が必要なのです。 ポインタのつなぎ変えのためです。 ステップ数を少なくするなら、 return head=p; とかは考えられますが、ステップ数が減るだけであり、 処理を減らすことは出来ないでしょう。

stbsheth
質問者

お礼

やっぱり僕がまだ未熟でしたね。 ありがとうございます。

stbsheth
質問者

補足

やっぱりやっぱり分かりません。   head = p;  /* 新たな領域を先頭ポインタに */   return head; の2行を   return p; にして試しましたが、僕の予想通りの動作でした。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • C言語の自己参照型プログラムについて

    #include <stdio.h> #include <string.h> #include <stdlib.h> struct list { int key; /* キー */ char name[20]; /* 名前 */ struct list *next; /* 次のデータへのポインタ */ }; struct list *add_list(int key, char *name, struct list *head); void show_list(struct list *p); void free_list(struct list *p); int main(void) { struct list *head; /* 先頭ポインタ */ char name[20]; int key = 0; head = NULL; /* 先頭ポインタにNULLを設定 */ printf("キーと名前(MAX:19文字)を入力(終了:CTRL+Z)\n"); while (scanf("%d %s", &key, name) != EOF) { /* リストにデータを登録 */ head = add_list(key, name, head); } /* リストの表示 */ show_list(head); /* リストの開放 */ free_list(head); return 0; } /*** リストにデータを登録 ***/ struct list *add_list(int key, char *name, struct list *head) { struct list *p; /* 記憶領域の確保 */ if ((p = (struct list *) malloc(sizeof(struct list))) == NULL) { printf("malloc error\n"); exit(EXIT_FAILURE); } /* リストにデータを登録 */ p->key = key; strcpy(p->name, name); /* ポインタのつなぎ換え */ p->next = head; /* 今までの先頭ポインタを次ポインタに */ head = p; /* 新たな領域を先頭ポインタに */ return head; } /*** リストの表示 ***/ void show_list(struct list *p) { while (p != NULL) { /* 次ポインタがNULLまで処理 */ printf("%3d %s\n", p->key, p->name); p = p->next; } } /*** リストの開放 ***/ void free_list(struct list *p) { struct list *p2; while (p != NULL) { /* 次ポインタがNULLまで処理 */ p2 = p->next; free(p); p = p2; } } これを実行すると、 新しく入力された順にリストが表示されます。 そうではなく、キーの昇順に表示したいです。 どなたかそのように実行できるようにプログラムを書き換えてくれませんか? 図々しいですがよろしくお願いいたします。m(_ _)m

  • 難しい・・・

    待ち行列への挿入と削除をするプログラムです。 先頭のkeyはそれぞれ I…単語を待ち行列に追加 D…単語を待ち行列から削除 P…現在の待ち行列に並んでいる要素を表示 Q…終了 を意味し、 入力例として I lose I eow D dummy P dummy I pop P dummy Q dummy のようなものを入力した時 eow eow pop のように表示されるようにしたいと思っています。 しかしできません>< プログラムは以下の通りです。 #include <stdio.h> #include <string.h> #include <stdlib.h> struct q{ char key; char word; struct q *next; /* 次のデータへのポインタ */ }; struct q *add_q(char key, char *name, struct q *head); void show_q(struct q *p); void free_q(struct q *p); int main(void) { struct q *head; /* 先頭ポインタ */ char word[20]; char key = 0; head = NULL; while(1){ scanf("%c %s",&key,word); switch(key){ case 'I': head=add_q(key, word, head); case 'P': show_q(head); case 'D': free_q(head); case 'Q': exit(1); default: printf("This is unexpected command\n"); }} return 0; } /*** リストにデータを登録 ***/ struct q *add_q(char key, char *word, struct q *head) { struct q *p; /* 記憶領域の確保 */ if ((p = (struct q *) malloc(sizeof(struct q))) == NULL) { printf("malloc error\n"); exit(EXIT_FAILURE); } /* リストにデータを登録 */ p->key = key; strcpy(p->word,word); /* ポインタのつなぎ換え */ p->next = head; /* 今までの先頭ポインタを次ポインタに */ head = p; /* 新たな領域を先頭ポインタに */ return head; } /*** リストの表示 ***/ void show_q(struct q *p) { while (p != NULL) { /* 次ポインタがNULLまで処理 */ printf("%3d %s\n", p->key, p->word); p = p->next; } } /*** リストの開放 ***/ void free_q(struct q *p) { struct q *p2; while (p != NULL) { /* 次ポインタがNULLまで処理 */ p2 = p->next; free(p); p = p2; } }

  • リストの作成と出力(C言語)

    こんにちは<_ _> リストの問題についてなのですが出力と逆順に出力するプログラムで headと前のデータの間に新しいデータを追加するように作ったのですが 入力した値が帰ってきません・・・ http://www9.plala.or.jp/sgwr-t/c/sec15-5.html などを見て見ましたが原因がいまいちよくわかりません。 どなたか教えてください おねがいします<_ _> #include<stdio.h> #include<malloc.h> struct tfield{ char name[20]; char tel[20]; struct tfield *pointer; }; struct tfield *talloc(void); int main(void) { struct tfield *head,*p; char buffer[BUFSIZ]; head=NULL; while(1){ p=talloc(); if(scanf("%s",buffer) == EOF){ break; strcpy(p->name, buffer); } if(buffer == "^Z")break; printf(" "); if(gets(buffer) == EOF){ if(scanf("%s",buffer) == EOF){ break; strcpy(p->tel, buffer); } p->pointer=head; /*今までの先頭ポインタを次のポインタに*/ head=p; } p=head; while(p!=NULL){ printf("%6s %s\n",p->name,p->tel); p=p->pointer; } return 0; } struct tfield *talloc(void) /*記憶領域の取得*/ { return (struct tfield *)malloc(sizeof(struct tfield)); } 変数は変えるなとのことです。 あと、 「name tel name tel name tel ^z name tel name tel name tel」 と表示したいのですがCtrl+zを二回押さないとできません><、 当方プログラム1ヶ月の初心者です ご指導どうかよろしくお願いします<_ _>

  • 終了条件Ctrl+zについて,結果表示ついて(C言語)

    こんにちは<_ _> 自己参照構造体に関する問題を解いています。 入力したデータとは逆順につながったリストを作成するという 問題で、結果はともかく逆順表示に出力できましたが 微妙なところでNGが出ています。 まず、 (1)「Ctrl+z」一回で終了しなければならない    以下のプログラムでは二回Ctrl+zを入力しないと    入力が終了できません。 (2)入力時並列させること    以下のプログラムでは入力時    「name tell name tell ・ ・     Ctrl+z」    と入力しなければなりませんが 「name tell name tell」 という風に入力しなければなりません・・・ (3)変数を変化、増減させてはならない     並列させるために変数増やそうと思ったら     bufferだけでやらなければならないみたいです・・・ 三時間ぐらい調べて悩みましたがうまくいきませんでした どうしたら以上のようにできるのかご指導、ご鞭撻のほどを どうかよろしくお願いします<_ _> #include<stdio.h> #include<malloc.h> struct tfield{ char name[20]; char tel[20]; struct tfield *pointer; }; struct tfield *talloc(void); int main(void) { struct tfield *head,*p; char buffer[BUFSIZ]; head=NULL; while(1){ p=talloc(); if( p == NULL ){ printf( "メモリを確保できません。\n" ); _exit( 0 ); } if(scanf("%s",&buffer)==EOF){ break; strcpy(p->name, buffer); } buffer=0; if(scanf("%s",&buffer)==EOF){ break; strcpy(p->tel, buffer); } p->pointer = head; head = p; } if(head != NULL){ p=head; while(p!=NULL){ printf("%6s %s\n",p->name,p->tel); p=p->pointer; } } return 0; } struct tfield *talloc(void)    /*記憶領域の取得*/ { return (struct tfield *)malloc(sizeof(struct tfield)); }

  • リストの削除について(構造体)

    リストの削除のプログラムを実行して行ってみると、リストの削除処理中にプログラムが終わって変更後処理がうまく表示されません。どこが間違っているかが分からないしだいです。返答のほどよろしくお願いいたします。 #include<stdio.h> #include<malloc.h> #include<string.h> struct list{ char name[20]; int age; struct list *next; }; void main(void) { struct list *head, *p, *n, *old; char key[20]; /*ダミーノード作成*/ head = (struct list*)malloc(sizeof(struct list)); old = head; while(p = (struct list*)malloc(sizeof(struct list)), printf("name age入力\n"), scanf("%s %d", p -> name, &p -> age) != EOF){ old -> next = p; old = p; } free(p); old -> next = NULL; p = head -> next; printf("変更前リスト\n"); while(p != NULL){ printf("name:%s age:%d\n",p -> name, p -> age); p = p -> next; } printf("削除key入力(name)\n"); gets(key); n = head; while(n != NULL){ old = n; n = n -> next; //printf("n -> name %s\n", n -> name); if(strcmp(n -> name, key) == 0){ printf("%s削除\n", key); //printf("n -> name %s old -> name %s\n", n -> name, old -> name); old -> next = n -> next; } } p = head -> next; printf("変更後リスト\n"); while(p != NULL){ printf("name:%s age:%d\n", p -> name, p -> age); p = p -> next; } }

  • mallocとfree

    struct list *p; /* 記憶領域の確保 */ if ((p = (struct list *) malloc(sizeof(struct list))) == NULL) { printf("malloc error\n"); exit(1); } とサンプルプログラムがあるのですが、if分の意味がわかりません。 また、mallocを使った場合freeで開放とあるのですが、 どういう意味なのかわかりません。 よろしければ、上記2つの点について教えてください。

  • LinkList使い方の意味がわかりません。。。。。

    LinkList使い方の意味がわかりません。。。。。 とあるサイトで以下のソースを目にしました。 ※以下参照 struct LIST * newList( struct LIST ** pstart){ struct LIST *p, *new; p = *pstart; new = (struct LIST *) malloc(sizeof(struct LIST)); new->next = NULL; if(p!=NULL){ /* リストに要素が存在するとき */ while(p->next!=NULL){ /* リストの末尾を探す */ p=p->next; } p->next = new; /* リストの末尾につなげる */ }else{ *pstart = new; /* 最初の要素の場所を *pstart に */ } return new; } そもそもここでnew->next = NULL;やってますけどmallocされた時点で実値は格納されていない と思うので必然的にNULLポインタになると思っていますが。。。(認識違いますか?) 誰か上記のこと含めLinkListに詳しい方使用方法(考え方)をご教授ください。

  • パスを含むファイルリストを表示する際、 ASCIIコード順になるように

    パスを含むファイルリストを表示する際、 ASCIIコード順になるようにソートして表示するようにするプログラムを作っているのですが、リストの仕組がいまいちよくわからず、うまくソートできません。 /* insert_dirent_cell(): セルをリストの先頭に追加する * 引数: head -- リストの先頭のセルへのポインタ * cell -- リストに加えたいセルへのポインタ * 戻り値: リストの先頭のセルへのポインタ */ struct dirent_cell *insert_dirent_cell(struct dirent_cell *head,struct dirent_cell *cell) { struct dirent_cell *ins_buf; //最初に挿入 if(head == NULL) { head = cell; return cell; } //先頭に挿入 if(strcmp(head->name, cell->name) > 0) { cell->next = head; head->next = cell->next->next; return cell; } //途中に挿入 ins_buf = head; while(ins_buf->next != NULL) { if(strcmp(ins_buf->next->name, cell->name) > 0) { cell->next = ins_buf->next; ins_buf->next->next = cell->next->next; return cell; } ins_buf = ins_buf->next; } //最後に挿入 ins_buf->next = NULL; cell->next = ins_buf; return cell; }

  • ポインターの繋ぎ換え

    動的に確保した領域にデータを入れ、 入れたデータをソートするプログラムを作っているのですが、 ソートする際に(入力3データ)→[head=p->next]→(入力2データ)→[head=p->next]→(入力1データ)→[head=p->next]と最後まで見た場合に再度先頭に戻る方法が分からないのでご教授お願いします。 ■イメージ head=NULL p 動的な領域の確保(p) (入力1)|no.1|Name1|struct sample *next| p->next = head /* NULLを入れる */ head = p; /* 入力したデータを入れる */ 動的な領域の確保(p) (入力2)|no.2|Name2|struct sample *next| p->next = head /* 前回の[入力1]のデータを入れる */ head = p; /* 入力2で入れたデータを入れる */ 動的な領域の確保(p) (入力3)|no.3|Name3|struct sample *next| p->next = head /* 前回の[入力2]のデータを入れる */ head = p; /* 入力3で入れたデータを入れる */ *現状* (入力3)-(入力2)-(入力1)-NULL ↑最後に入力したデータ (降順にソート後) |no.3|Name3|struct sample *next| 動的な領域の確保 |no.2|Name2|struct sample *next| 動的な領域の確保 |no.1|Name1|struct sample *next| 環境:WinXP-SP3 コンパイラ:VisualC++ExpressEdition2008 使用関数:malloc 以上、宜しくお願いします。

  • (構造体)双方向連結リストの作成!

    ダミーノードを先頭に、双方向連結リストを作成したいのですがなかなかうまくできません。とりあえず、ダミーノード無しのものはなんとか出来ましたが、循環連結がうまくいっていない次第です。 どうかお力添え願います。 #include<stdio.h> #include<malloc.h> #include<process.h> typedef struct node{ struct node *left; char name[20]; int age; struct node *right; }NODE; NODE *memalloc(void); void main(void) { NODE *head, *tail, *p; tail = NULL; while(p = memalloc(), printf("名前 年齢入力(Ctrl + Zで終了)>"), scanf("%s %d", p -> name, &p -> age) != EOF){ p -> left = tail; tail = p; } p = tail; head = NULL; while(p != NULL){ p -> right = head; head = p; p = p -> left; } head -> left = tail; p = head; printf("リスト表示\n"); while(p != NULL){ printf("名前:%20s 年齢:%5d\n", p -> name, p -> age); p = p -> right; } } NODE *memalloc(void) { NODE *ptr; if((ptr = (NODE *)malloc(sizeof(NODE))) != NULL){ return ptr; } printf("\n動的メモリ割当に失敗しました。\n"); exit(1); return 0; }