ショッピングモール  


【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※マルチポスト(多重投稿)は謹んで!

 詳しくはこちら



 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板1こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧

No.3076

自己参照構造体とポインタのポインタ
投稿者---HCS(2004/11/26 16:41:12)


以下のページを参考にして双方向リスト構造体を作成したのですが、
どうも関数へ渡すポインタのポインタの部分が
理解不足のせいか納得できなくて悩んでいます。

http://www9.plala.or.jp/sgwr-t/c/sec15-5.html

どこかおかしいところあるでしょうか?
うまく動作してしまったのでこれでよいのかもしれませんが、
おかしなとこがありましたらご指摘していただけると嬉しいです。


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

struct list {
    int key;            /* キー */
    char name[20];    /* 名前 */
    struct list *before;        /* 前のデータへのポインタ */
    struct list *next;    /* 次のデータへのポインタ */
};

/*** リストにデータを登録 ***/
struct list *add_list(int key, char *name, struct list *head, struct list **finish){
    
    struct list *p;
    
    /* 記憶領域の確保 */
    if ((p = (struct list *) malloc(sizeof(struct list))) == NULL) {
        printf("malloc error\n");
        exit(1);
    }
    
    /* リストにデータを登録 */
    p->key = key;
    strcpy(p->name, name);
    
    /* ポインタのつなぎ換え */
    p->next = head;  /* 今までの先頭ポインタを次ポインタに */
    
    if(p->next != NULL){
        p->next->before = p;
    }
    else{
        *finish = p;
    }
        
    p->before = NULL;
    head = p;         /* 新たな領域を先頭ポインタに */
    
    return head;
}

/*** リストの表示 (入力順) ***/
void shows_list(struct list *p){
    
    while (p != NULL) { /* 次ポインタがNULLまで処理 */
        printf("%3d %s\n", p->key, p->name);
        p = p->before;
    }
}

/*** リストの表示 (入力逆順) ***/
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;
    }
}

main(){
    struct list *head;    /* 先頭ポインタ */
    struct list *finish;        /* 終了ポインタ */
    char name[20];
    int key = 0;
    
    head = NULL;        /* 先頭ポインタにNULLを設定 */
    finish = NULL;
    
    printf("キーと名前(MAX:19文字)を入力(終了:CTRL+Z)\n");
    while (scanf("%d %s", &key, name) != EOF) {
        /* リストにデータを登録 */
        head = add_list(key, name, head,&finish);
    }
    
    /* リストの表示(入力順) */
    shows_list(finish);
    puts("\n");
    /* リストの表示(入力逆順) */
    show_list(head);
    
    /* リストの開放 */
    free_list(head);
}



この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:自己参照構造体とポインタのポインタ 3090 RAPT 2004/11/27 12:42:35


No.3090

Re:自己参照構造体とポインタのポインタ
投稿者---RAPT(2004/11/27 12:42:35)


>どうも関数へ渡すポインタのポインタの部分が
>理解不足のせいか納得できなくて悩んでいます。

どの辺がどう納得できないのですか?

これこれがこうなるのが正しいらしいが納得できない。
自分はこう思うのだが、なぜ違うのか。

などといったように書かないと回答できないかと。


この投稿にコメントする

削除パスワード

No.3091

Re:自己参照構造体とポインタのポインタ
投稿者---HCS(2004/11/29 09:33:00)


質問の仕方が悪くてすみませんでした。
聞きたいことは

head = add_list(key, name, head,&finish);

このadd_list関数を呼び出している部分で、
引数で&finishを渡しているのですが、
この&finishは一体何を表しているのか自分で作ったのにいまいち
理解できずに困っています。
finishというのは構造体へのポインタで、
&というのはアドレスを示すのですよね?
どう解釈すればよいのでしょうか?






この投稿にコメントする

削除パスワード

No.3092

Re:自己参照構造体とポインタのポインタ
投稿者---朱鷺(2004/11/29 10:58:42)


>質問の仕方が悪くてすみませんでした。
>聞きたいことは
>
>head = add_list(key, name, head,&finish);
>
>このadd_list関数を呼び出している部分で、
>引数で&finishを渡しているのですが、
>この&finishは一体何を表しているのか自分で作ったのにいまいち
>理解できずに困っています。
>finishというのは構造体へのポインタで、
>&というのはアドレスを示すのですよね?
>どう解釈すればよいのでしょうか?
>
>
>

finishは一番最初に追加したデータのアドレスをさします。
どうやら全部がポインタで制御されているので、&は使わなくてもOKです。
ちなみに、add_listの最後の引数をポインタのポインタにする必要はあまり
ないかもしれません。



この投稿にコメントする

削除パスワード

No.3095

Re:自己参照構造体とポインタのポインタ
投稿者---YuO(2004/11/29 13:59:25)


>head = add_list(key, name, head,&finish);
>このadd_list関数を呼び出している部分で、
>引数で&finishを渡しているのですが、
>この&finishは一体何を表しているのか自分で作ったのにいまいち
>理解できずに困っています。

finishという名前の付いたオブジェクトへのポインタを生成しています。


>finishというのは構造体へのポインタで、
>&というのはアドレスを示すのですよね?

finishというのは,struct listへのポインタ型のオブジェクトに付いた名前です。
単項の&演算子は,引数を参照するポインタを生成する演算子です。
#「アドレスを示す」というのは意味不明。


add_list関数の内部でfinishという名前の付いたオブジェクトの値そのものを変更する処理があるために,
add_list関数にポインタで渡しています。



この投稿にコメントする

削除パスワード

No.3107

Re:自己参照構造体とポインタのポインタ
投稿者---HCS(2004/11/30 09:29:29)


みなさん回答ありがとうございます。
ただちょっとわからないところがありまして、

>単項の&演算子は,引数を参照するポインタを生成する演算子です。

この部分の引数を参照するポインタを生成する演算子って
いまいち理解できないのですが
どう解釈すればよいのでしょうか・・・?



この投稿にコメントする

削除パスワード

No.3109

Re:自己参照構造体とポインタのポインタ
投稿者---YuO(2004/11/30 10:10:44)


>この部分の引数を参照するポインタを生成する演算子って
>いまいち理解できないのですが
>どう解釈すればよいのでしょうか・・・?

そのまんまですが……。

T foo;

と宣言されていた場合に,部分式
&foo

は,型
T *

を持つ,「fooを参照するポインタ」を生成します。
#ポインタとは,「関数もしくはオブジェクトを参照するための値」です。



この投稿にコメントする

削除パスワード

No.3110

Re:自己参照構造体とポインタのポインタ
投稿者---かずま(2004/11/30 12:01:23)


> この部分の引数を参照するポインタを生成する演算子って
> いまいち理解できないのですが
> どう解釈すればよいのでしょうか・・・?

規格書に書かれている記述よりももっと難しい表現ですね。
規格書にはもっと分かりやすく書かれています。

: 6.5.3.2 アドレス及び間接演算子
: 意味規則 単項&演算子は, そのオペランドのアドレスを返す。

main で宣言されている head と finish はそれぞれリストの先頭要素と
末尾要素を指すためのものですよね。私なら finish よりも tail という
名前にしますが、まあ、それはどうでもよいことです。

最初は、リストの要素数がゼロなので、head も finish も何も指すものが
なく、NULL が入っています。

add_list() で一つ目の要素をリストに追加するとき、head と finish の
両方を変更しなければなりません。add_list() が head と finish の値を
もらっても、main() の head と finish 自身を変更することは出来ません。
そこで、add_list() は、変更した head の値を関数の返却値とすることで、
main() 側で head を変更しています。しかし、finish の値を変更するため
には、finish 自身の値ではなく finish のアドレスが必要になるわけです。



この投稿にコメントする

削除パスワード

No.3114

Re:自己参照構造体とポインタのポインタ
投稿者---かずま(2004/11/30 13:47:21)


> main で宣言されている head と finish はそれぞれリストの先頭要素と
> 末尾要素を指すためのものですよね。私なら finish よりも tail という
> 名前にしますが、まあ、それはどうでもよいことです。

finish 以外にも before に違和感を覚えます。
before の反対は after ですから、next の反対は previous、略して prev
のほうが私の好みであり、一般的です。

main() で head, tail, key, name を用意しますよね。
これって、struct list の中身とよく似ていませんか?
そこで、これらを struc list のダミー要素として持っておき、さらに
リストを環状にすると、add_list() が簡単になります。

それから、struct list は、Node という名前にして、プログラムを書き直
してみました。
#include <stdio.h> 
#include <stdlib.h>
#include <string.h>

typedef struct Node {
    int  key;
    char name[20];
    struct Node *prev;
    struct Node *next;
} Node;

void init_list(Node *lp) { lp->prev = lp->next = lp; }

void add_list(Node *lp, Node *dp)
{
    Node *p = malloc(sizeof(Node));
    if (p == NULL) puts("out of memory"), exit(1);
    *p = *dp;
    p->prev = lp->prev,  p->next = lp;
    lp->prev->next = p,  lp->prev = p;
}

void show_list(Node *lp)
{
    Node *p;
    for (p = lp->next; p != lp; p = p->next)
        printf("%3d %s\n", p->key, p->name);
}

void show_list_rev(Node *lp)
{
    Node *p;
    for (p = lp->prev; p != lp; p = p->prev)
        printf("%3d %s\n", p->key, p->name);
}

void free_list(Node *lp)
{ 
    Node *p, *p2;
    for (p = lp->next; p != lp; p = p2)
        p2 = p->next, free(p);
}

int main(void)
{
    Node data;
    init_list(&data);
    puts("キーと名前(MAX:19文字)を入力(終了:CTRL+Z)");
    while (scanf("%d %19s", &data.key, data.name) == 2)
        add_list(&data, &data);
    puts("---"), show_list(&data);
    puts("---"), show_list_rev(&data);
    free_list(&data);
    return 0;
}



この投稿にコメントする

削除パスワード

No.3117

Re:自己参照構造体とポインタのポインタ
投稿者---HCS(2004/11/30 18:00:45)


詳しい説明、プログラムとても参考になりました。
どうもいろいろとありがとうございました。


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧




掲示板提供:Real Integrity