C言語の構造体についての参照方法について

このQ&Aのポイント
  • C言語の構造体内での数の格納方法について、配列ではなく可変長で格納する方法について教えてください。
  • struct LIST内のstruct Numの数値を参照する方法が分かりません。どのようにプログラムを記述すればいいでしょうか。
  • C言語の構造体内での数値の参照方法について詳しく教えてください。
回答を見る
  • ベストアンサー

C言語の構造体についてなんですが。

struct LIST {     struct Num* number;     struct LIST* next;/* 次の要素へのポインタ */ }*root; struct Num{     int a;     struct Num* next; /* 次の要素へのポインタ */ }*numroot; と構造体を定義したときに、 main(){     struct LIST *p;     for(p = root; p != NULL; p = p->next) ; } とすれば、pの先頭からNULLまでを参照していくことは分かるんですが、pのnumberの先頭からNULLまでの参照方法(プログラムのfor文の記述方法)がイマイチわかりません。つまり、構造体の構造体をどのように参照するかということです。 これを実現したい理由は、構造体内での数の格納を配列(固定長)ではなく可変長で格納したいからです。 分かる方は解答をお願いします。

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

  • ベストアンサー
回答No.2

こういうことを聞いていますか? int main(void) {     struct LIST *p;     struct Num *q;     /* make the list structure some how */     for (p = root; p != NULL; p = p->next)         for (q = p->number; q != NULL; q = q->next) {             /* do something */         }     return 0; } 余談ですが、「構造体内での数の格納を可変長で格納したい」という要求だけなら配列を動的に割り当てるという実装もあります。数を格納するためのオーバヘッドはこちらの方が少ないですし、ポインタの操作をあまりゴリゴリしないでいい分だけバグを作りにくいというメリットがあります。その反面、格納している配列を縮小したいときに再度メモリーを割り当てて移し変えるという面倒くさいことをしないといけなくなるという欠点もあります。また、リストを使う典型的な理由として要素の順序を自由に入れ替えたいというのもあると思いますが、配列を使うとこの点も不利ですね。

tommyking3
質問者

お礼

まさにそうです。スミマセン分かりにくい説明で。回答を読んで解決できました。 迅速かつ親切な回答ありがとうございました。 そういえば余談を見て、昔授業で配列の動的割り当てについてやったのを今思い出しました(^^;)

その他の回答 (4)

回答No.5

#3です。 コピーミスです。 if((p=(LIST *)malloc(…))=NULL) return NULL; root.last->next = p; root->last = p; return p; は if((p=(LIST *)malloc(…))=NULL) return NULL; root.last->next = p; root->last = p; p->next = &root.LIST;//追加 return p; リング構造にするとよい場合が多いのは、リストを検索する場合 終端をNULLにするとループ内での条件判定が2回になります。 for(p = root->next;p != NULL;p=p->next) if(検索条件と一致) break; return p; リング構造にすると、ループ内での条件判定が1回で済みます。 rootを検索条件と一致するように書き換える。 for(p = root.next;検索条件と一致しない;p=p->next) ; return root ? NULL : p; リストの数が多い場合に、実行速度に差がでます。 Tacosanへ 構造体の中にポインタとして宣言した場合と配列と宣言した場合では、 C言語の書き方は同じでも、実行速度が異なります。 以下のようにaの11番目のデータにアクセスする場合、 ポインタでも配列でも同様に以下のように書けますが p->a[10] (1)配列と宣言した場合。 他の構造体のメンバーと同様に、 p+[オフセット] でアクセス可能。32bitのCPUの場合 p+[8+40] (2)ポインタで宣言した場合 同様に32bitとすると aの値を読み込む a=p+[8] 次に a+[40] とアクセスする。 ということで、ポインタにすると メモリアクセスが一回増える ということです。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

今の C なら struct List { struct List *next; int size; int a[]; }; として struct List *p = malloc(sizeof *p + sizeof *p->a * size); p->size = size; のようにするのがより適切でしょう>#3. 構造体の最後のメンバに限り「大きさを明示しない」ことが可能になっています.

回答No.3

配列を使った大きさの異なる構造体を動的に割り付ける方法は 以下の2つの構造体を統合して struct LIST { struct Num* number; struct LIST* next;/* 次の要素へのポインタ */ }*root; struct Num{ int a; struct Num* next; /* 次の要素へのポインタ */ }*numroot; このようにします。 struct LIST { struct LIST* next;/* 次の要素へのポインタ */ int cont; /*配列の要素の数*/ int a[1]; /*配列の実態*/ }; 配列のサイズをnとすると LIST *list; list->next=malloc(sizeof(*list->next)+sizeof(list->a[0])*(n-1)); list->cont = n; のように動的にメモリ領域を確保します。 リンクリストの構造体の大きさがことなるパターンは 頻繁に存在します。 また、rootを次のように設定してリング構造にすると static struct { LIST list; List *last; /*終端へのポインタ*/ } root = {{&root.list, -1, 0}, &root.list}; もっとも頻繁に実行されるリストの追加が高速化できます。 LIST *p; if((p=(LIST *)malloc(…))=NULL) return NULL; root.last->next = p; root->last = p; return p; 終端のリストかどうかの判定は if(list->next.cont < 0) //リストの終端 削除は、lastの処理が増えます。 削除や移動を頻繁に行うのであれば 双方向リンクが圧倒的に高速です。 LIST *list; //削除するリスト LIST *p; if((p = root.next) == &root) return -1;//リンクが空かどうかの判定 while(p->next != list) //自分自身を参照しているリストを探す p = p->next; p->next = list->next; if(root.last == list) root.last = p->next; free(list); return 0;

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

「pのnumberの先頭からNULLまでの参照」とはどういうことでしょうか? リンクリストをたどるだけなら, 何も困ることはないと思うんだけど. p->number でリストの先頭要素がわかるよね.

tommyking3
質問者

補足

はい、そこはわかるんですが、p->numberの先頭要素からNULLまでのfor文の条件をどのように書くのかって部分でちょっと困ってます。 main(){     struct LIST *p;     for(p = root; p != NULL; p = p->next){       for(?){         printf("%d\n",p->number->a);       }     } } というように書いたときに、「?」部分をどのように書けばいいのかで困ってます。

関連するQ&A

  • 構造体について

    typedef struct num{ char rv[1000]; struct number *next; struct number *prev; }Num; このような双方向のリスト構造に void aplist(Num **s,char ns[]){ Num *old, *new; if(*s==NULL){ *s = (Num *)malloc(sizeof(Num)*1); strcpy((*s)->rv,ns); (*s)->next = NULL; (*s)->ago = NULL; return;} old= (*s); while(old->next != NULL){ old = old->next;} new = (Num *)malloc(sizeof(Num)*1); strcpy(new->rv,ns); new->next = NULL; old->next = new; new->prev = old; 関数の中でこのようにnext,oldを指定していったのですが、これでよいでしょうか?また、このリストを逆方向(prevの方向)に表示していきたいのですがどのように書けばよいでしょうか?nsは1行の文字列で、各構造体に1行ずつ入れていっています。

  • C言語 リスト

    (1) /* list.c */ #include <stdio.h> #include <stdlib.h> struct node { int num; struct node *next; }; main() { struct node *head, *p; int x; head = NULL; while(scanf("%d",&x) != EOF) { p = (struct node *)malloc(sizeof(struct node)); p->num = x; p->next = head; head = p; } // リストの要素のnumの値を先頭から順に表示する p=head; while(p!=NULL) { printf("%d\n" ,p->num); p = p->next; } } (2) struct node *q; q = head; while(q->next != NULL) q = q->next; (1)を(2)を使い新しいノードをリストの最後に追加するようにしたいのですが どう書いたら良いのか教えていただきたいです

  • 構造体について

    構造体について分からない点があり,教えて頂きたく投稿いたします. 現在,以下のような構造体を作成しています. typedef struct{ int data; struct node *NEXT; }node; また,それを管理するためのリストを以下の構造体にて宣言しました. typedef struct{ node *crnt; node *last; }node_list; また,使用する関数内での宣言は以下の通りです. node_list *non_dscvr_node_list; //未探査ノードを格納する node *crt_dscvr_node; //現在探査中のノードを示す node *start; そして, start->data = 10; start->NEXT = NULL; non_dscvr_node_list->crnt = start; non_dscvr_node_list->last = start; : : crt_dscvr_node = non_dscvr_node_list->crnt; non_dscvr_node_list->crnt = crt_dscvr_node->NEXT; //ここでエラーがでる. エラーの詳細は以下の通りです. warning: assignment from incompatible pointer type 私としては,リストに格納されている先頭ノードをポップして,次のノードを先頭にしたつもりだったのですが,ポインタタイプに互換性がないと怒られてしまいました. 少し調べては見ましたが,nodeの構造体を管理するために別に構造体を定義しているページがあまり見あたりません. 従って,そのようなページがあれば教えて頂きたいと思います. このような方法はあまりよくないのでしょうか. 併せて教えていただけますようお願いいたします.

  • 構造体とポインタ配列

    現在C言語の勉強をしております。 環境はwindowsXP、コンパイラはVC6.0です。 構造体と、ポインタの配列についてなのですが、 以下のような構造体が宣言されている時に、リスト構造にデータがいくつか入っているとします。 // 構造体 typedef struct address { unsigned char names[NAME_SIZE+1]; /* 名前 */ char tels[TEL_SIZE + 1]; /* 電話番号 */ struct address *prev; /* 前へのポインタ */ struct address *next; /* 次へのポインタ */ }Address, *a_pt; そのリスト構造を先頭要素か順番にポインタ配列に格納するには以下の方法ではおかしいでしょうか? /* ポインタ配列を用意する */ Address *array[MAX_COUNT]; /* top_ptは先頭のポインタです */ pt = top_pt; /* データがなくなるまで配列へ格納する */ while(pt != NULL){ array[count++] = pt; pt = pt->next; } /* 配列の最後はNULLとする */ array[count] = NULL; また、配列の中身を確認する方法としては、 printf("配列の中身:%s\n", array[0]->names); では、アドレスが表示されてしまうのかな・・と思ったら、accessViolationで落ちてしまいました・・・。 中身はどうしたらデバッグ出来ますでしょうか? そもそも、以下の2つは何か違いはありますか? Address *ptA[100]; a_pt ptB[100]; 皆さん、どうかよろしくお願いいたします。 理解不能な場合はご指摘ください。

  • [C言語] NULLは必ず0(番地)ですか?

    趣味でプログラムを勉強しております。 とある書籍にてリスト構造を辿るソースが以下の様になっていました。 struct List{  省略  struct List *next; }; struct List *p; struct List *header; /* ここでリスト構造作成 */ /* リスト構造を辿る */ for(p = header; p; p=p->next) リストの最終要素のnextはNULLを指しています。 私の環境ではNULLは0(番地?)を指していたので for文のループ継続条件として上記の書き方で動作しました。 質問なのですが、他の環境でもこの書き方で正常に動作するのでしょうか? つまりNULLは必ず0(番地?)を指していることが保証されているのでしょうか。 それとも以下の様に書いた方が良いのでしょうか? for(p = header; p != NULL; p=p->next) よろしければ、ご教示いただけますと幸いです。

  • リスト構造のプログラミング

    #include<stdio.h> #include<stdlib.h> typedef struct number{ float x; struct number *next; }Num; void append_list(Num **, float); int main(void) { Num *start,*p; float i,d; start=NULL; for(i=0.0;i<10.0;i++){ append_list(&start,i); } p=start; while(p!=NULL){ printf("%f\n",p->x); p=p->next; } p=start; while(p !=NULL){ Num *q; q=p; p=p->next; free(q); } return 0; } void append_list(Num **s, float n) { Num *end, *new; if(*s==NULL){ *s=(Num *)malloc(sizeof(Num)*1); (*s)->x=n; (*s)->next=NULL; return; }end=(*s); while(end->next !=NULL){ end=end->next; } new=(Num *)malloc(sizeof(Num)*1); new->x=n; new->next=NULL; end->next=new; } 0~9までの数値を順番に追加してリスト構造のデータ構造で 保存するプログラミングを作ったのですが、これにキーボード から入力した1つの実数(0~9)を数値の順序を乱さないよう にその数値を持つ要素を追加するにはどうすればよいのでしょうか?

  • 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

  • 構造体のポインタにNULLが入らない

    typedef struct tag{ int number; char name[10]; struct tag *next; }DATA; という構造体があって、 DATA *p; と宣言し、 p->next == NULL; とすることはできないんですか? セグメンテーション違反になってしまうのですが。 pが指すnextにNULLを入れるにはどうしたらいいのでしょうか?

  • 構造体の中の構造体

    typedef struct number{ int x; struct number *next; }Num; 初心者な質問で申し訳ないんですが、構造体の中に構造体があるのはどう解釈していいんでしょうか? typedef struct number{ int x; int y; }Num; の場合はNum a,b;がint a.x,a.y,b.x,b.yとなるのは分かるんですが・・・

  • C言語の問題について

    #include <stdio.h> #define NMAX 20 struct node_tag { ??????? }; int main(void) { ??????? struct node_tag *p; int i; for (???????) { ??????? } p = ???????; while (p!=NULL){ printf("%d %s\n", ???????, ???????); p = ??????? } return 0; } 「 日付(整数) と 曜日名(文字) と 次の要素を指すポインタ 」を要素に持つ自己参照構造体 struct note_tag を定義して、この構造体を利用して線形リストを作成し,日付と曜日を表示させるプログラムを作成したいのですが?がわからなくて困ってます・・・どうか教えてください