- 締切済み
このソースの意味を教えてください
構造体を使ったリストを理解するためのソースで 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; }
- みんなの回答 (10)
- 専門家の回答
みんなの回答
- Lchan0211
- ベストアンサー率64% (239/371)
回答1、回答2は勘違いされているので無視していいとして、 私もadd_list()に必要な機能としては、確かに無駄な処理だと思います。 が、「トップキューイン処理」の定石コーディングとしては、 p->next = head; head = p; の2行をセットで記述するものと考えた方がよいと思いますよ。 例えば、今のadd_list()を拡張して、1度に複数個のリストを追加するような 関数を作りたくなった場合、今のコーディングなら、 malloc行~head=p行までをfor文で囲むコーディングを 追加するだけで済みます。 リスト処理の定石に従ってコーディングしていれば、このような拡張に 対応しやすくなります。 解説サイトにあるような片方向リストは、まだ簡単なのですが、 双方向リスト等の複雑なリスト構造になると、 処理すべきポインタが増えるので、 ・トップキューイン処理 ・ラストキューイン処理 ・中間挿入処理 ・リスト削除処理 といったそれぞれのリスト処理を定石パターン化すると、 ミスを減らすことができます。 一見、無意味な勘違いコーディングをしているようにも見えますが、 ミスのないコーディングをするために、多少無駄でも 定石パターンを大事にしてコーディングすることは、 とても有意義なことだったりします。
- S117
- ベストアンサー率40% (18/45)
まず前提としてこのソースは「リストの説明をするためのソース」です。 で、問題の部分 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)
まあ、return p; でもいいんでしょうが、 実際のところ、最近のコンパイラであれば、ほぼ間違いなく head = p; return head; も、 return p; もコンパイル結果は全く同じだと思われます。 なんで、スタイルというか、思考過程が違うってことで、でいいではないでしょうか。 ・リストの先頭は常にheadで、挿入処理に伴ってheadを更新した、て思えば、質問文のようなコードになるでしょうし、 ・リストの先頭がheadからpに変わった、って思えば、return p; になるでしょう。
- mk48a
- ベストアンサー率56% (1133/2007)
#3,4です。 >可読性を向上という意味ですか。 >僕には、ソースを書いた人が何か勘違いをしているように見えるんですよ。 ソースを書いた人の意図はソースを書いた人にしかわかりませんので、個人の意見です。 最近は、無駄を省いたプログラムよりも可読性を重視したメンテナンスの容易なプログラムの方が好まれる傾向にあります。 私が作者の記述方法にも一定の利点があると思うのは、ポインタのつなぎ換えの処理の後にリストの先頭に関する操作を追記する必要があった場合に、わかりにくいpではなくheadの変数でアクセスできるので、メンテナンスが容易である点です。(headはリストの先頭なので) 個人で作っているプログラムなら自分で把握できていると思いますが、大規模プログラムを分担してコーディングしているとわかりやすい変数名はバグの抑止にも役立ちます。(なので、pという変数名はわかりづらいので使わない) また、自分で作ったプログラムでも、後から読み返す際に忘れてしまっていることなどよくあります。 まぁ、未熟云々ではなくてコーディングスタイルの問題なので、「この人はこう書くんだ」くらいに思っているのが良いと思います。 苦労して無駄を省いたコードを書いても、コンパイルしてみたら結果は同じだったなんてことは良くありますし。
- Tasuke22
- ベストアンサー率33% (1799/5383)
> やっぱり僕がまだ未熟でしたね。 いえ、私の方が未熟のようです。 お二方がheadの内容は変わらないと仰っています。 私は思考放棄です(^^;) 失礼しました。
- zwi
- ベストアンサー率56% (730/1282)
あっ、struct list **headじゃありませんでしたね。 失礼しました。
- mk48a
- ベストアンサー率56% (1133/2007)
#3です。追加。 この関数の役割を考えてみましょう。 この関数は、「keyとnameとリストの先頭ポインタを受け取って、新しい構造体を追加し、リストの先頭ポインタを返す関数」です。 引数struct list *headはスタック上にコピーが生成されるので、この関数が終了したらクリアされます。 なので、この関数内でheadをいくら変更してもこの関数の呼び出し元での変化はありません。 呼び出し元ではこの関数の戻り値を新しくリストの先頭として使用します。 個人的な感想としては、リストの先頭ポインタがころころ変わる変数を生で使うのはなんとなく気持ち悪いので、このような書き方はしませんが、リスト構造の処理としては普通の処理です。
お礼
ありがとうございます。
- mk48a
- ベストアンサー率56% (1133/2007)
ステップ数を減らすなら、 head=p; の行を削除して、 return p; で良いですが、まぁ、この関数の中ではheadはリストの先頭としているようなので、ポインタをつなぎ変えた後に head=p; とするのは、可読性を向上させる意味ではアリだと思います。 私ならリストを管理する構造体を作ってそのポインタを受け渡ししますが、小さなプログラムならば特に問題は無いと思います。
お礼
ありがとうございます。 可読性を向上という意味ですか。 僕には、ソースを書いた人が何か勘違いをしているように見えるんですよ。
- zwi
- ベストアンサー率56% (730/1282)
きわめて合理的なプログラムですよ。 head = p; /* 新たな領域を先頭ポインタに */ を消して、 return p; だけにしてみたら分かりますがshow_listもfree_listも、まともに動作しなくなります。 これがリスト構造ですので、もう一度ちゃんと調べてみてください。 しかし、疑問に思ったらなぜ試してみないで、この掲示板に質問する人が多いのだろう・・・。試せばすぐ分かるのに。
補足
ちなみにあなたは実際に head = p; /* 新たな領域を先頭ポインタに */ return head; の2行を return p; にして試してまともに動作しなくなることが確認できたのでしょうか? 僕の方ではまともに動作しているように見えました。
- Tasuke22
- ベストアンサー率33% (1799/5383)
return head; だから head=p; が必要なのでは ありません。 元々 head=p; が必要なのです。 ポインタのつなぎ変えのためです。 ステップ数を少なくするなら、 return head=p; とかは考えられますが、ステップ数が減るだけであり、 処理を減らすことは出来ないでしょう。
お礼
やっぱり僕がまだ未熟でしたね。 ありがとうございます。
補足
やっぱりやっぱり分かりません。 head = p; /* 新たな領域を先頭ポインタに */ return head; の2行を return p; にして試しましたが、僕の予想通りの動作でした。
お礼
あれが不自然に見えるという方が他にいなければ、不自然に見えてしまう僕は やっぱり未熟なんでしょうね。 ありがとうございます。