質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

1301閲覧

c言語 リスト構造 入力した値を最後尾へ持っていくプログラム

homiru

総合スコア1

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2022/12/05 06:29

前提

ここに質問の内容を詳しく書いてください。

はじめてこのサイトを使うので、不慣れです。
至らぬ点が多くあると思いますが、宜しくお願いします。

C言語のリスト構造の最後尾についての質問です。
反復を用いて、計5回入力させて、それを表示するリストを作っています。
例えば、50 → 100 → 150 → 200 → 250と入力させると、
250
200
150
100
50
と表示されるリストです。

このリストを入力順(つまり新たに入力した要素を最後尾に)持っていくリストも一緒に作りたいのですができません。

エラーメッセージなどはありません。

このプログラムでは、入力順となっていて、
pNew->next = NULL;
これが最後尾となっています。
最後尾の次の要素はNULLとして置く、これが最後尾であるということだと学びました。
これと同じ要領で、新たに入力した要素を最後尾に持っていくためには、ダミーデータとして置いているlistHead.nextをNULLとして置くのではないかと考えています。
そうすれば、
ダミーデータ
50
100
150
200
250
となるのではないか、と思い作っていました。
しかし、そもそもどのようにして最後尾までリストを繋げたら良いのかがわかりません。
ネット上に上がっているリスト構造の最後尾のやり方についての記事をいくつか読み、それをもとに作ってもみましたが、できませんでした。

実現したいこと

・入力したデータが最後尾となるようなリストにしたい
・このプログラムを大幅に改変させずにつくりたい
(今質問していることも含めて、かなり考えて自分の納得のいくプログラムを作ったつもりです。ですので、かなり愛着があります。このプログラムの内容をほぼ全部潰して...というのは嫌です)
質問している身でありながら、わがままばかり言ってしまいすみません。

発生している問題・エラーメッセージ`

・エラーメッセージ等はでていません。

該当のソースコード

#include <stdio.h> #include <stdlib.h> struct list{ int element; struct list *next; }; typedef struct list List; void showList(List* pHead); int main(void){ List listHead; List *pNew; /*新しい要素ポインタ*/ List *pIns; /*要素の挿入場所になる要素のポインタ*/ List *p;   int inData, i; listHead.element=-1; listHead.next=NULL; for(i=0;i<5;i++){ printf("input data"); scanf("%d", &inData); printf("==================\n"); pNew=(List*)malloc(sizeof(List)); pNew->element=inData; pNew->next=NULL; pIns=&listHead; pNew->next = pIns->next; pIns->next = pNew; showList(&listHead); printf("==================\n"); } return 0; } void showList(List *pHead){ List *p; p=pHead; while(p!=NULL){ printf("element:%d\n",p->element); p=p->next; } return ; }

試したこと

pNew->next = NULL;
pIns = &listHead;
pNew->next = pins->next;
pIns->next = pNew;

これを逆にすればできるのではないかと思い、

listHead.next = NULL;

ここまでは分かったのですが、ここから先がまったくと言っていいほどわかりません。
色々並び変えてみたのですが、失敗に終わっています。

補足情報(FW/ツールのバージョンなど)

追加、削除、探索等のリストならではのものはひとまず置いておいて、まず入力した値を最後尾に持っていくプログラムを目指しています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dameo

2022/12/05 06:55 編集

単方向リスト構造なので、 listHead.element = -1 listHead.next = &最初にmallocしたList 最初にmallocしたList.element = 最初に入力した内容 最初にmallocしたList.next = &2番目にmallocしたList 2番目にmallocしたList.element = 2番目に入力した内容 2番目にmallocしたList.next = &3番目にmallocしたList 3番目にmallocしたList.element = 3番目に入力した内容 3番目にmallocしたList.next = &4番目にmallocしたList 4番目にmallocしたList.element = 4番目に入力した内容 4番目にmallocしたList.next = &5番目にmallocしたList 5番目にmallocしたList.element = 5番目に入力した内容 5番目にmallocしたList.next = NULL となるように変えればいいのだと思いますよ。 がんばってください。
mather

2022/12/05 09:48 編集

> 入力したデータが最後尾となるようなリストにしたい これは実現できているのでは?と思いましたが、 > このリストを入力順(つまり新たに入力した要素を最後尾に)持っていくリストも一緒に作りたいのですができません。 こちらの意味ですよね? 「リストを作りたい」のか「入力順に表示したい」のかでやることが変わりそうですが、どちらが実際にやりたいことでしょうか?
jimbe

2022/12/05 13:51 編集

(編集前は全面削除) 逆順は出来ているようですので、正順への改造を回答しました。
guest

回答3

0

ベストアンサー

常に pIns が最期の要素を指すようにすれば良いように思います。

  1. ループ内の pIns=&listHead;for(~){ の直前に移動。
  2. pIns->next=pNew; の後で pIns=pNew;

な感じでしょうか。

逆順なら pIns は常に listHead を指すだけなのでループの中でも外でも構いませんが、正順ならループで変わっていくのでループの外でする必要があります。
1 をやった後は 2 をやるかどうかだけで入力正順か入力逆順かが変わるという感じですね。

ついでに、 showList 内の p=pHead;p=pHead->next; が良いかと。ダミーデータは入力データじゃないですし。


最後尾の次の要素はNULLとして置く、これが最後尾であるということだと学びました。

学んだという表現に若干違和感があるのですが、NULL だから最後尾なのではありません。
『最後尾の next は NULL にする』と(このリストの仕様を決めた人が)決めたということです。
要は通常のリンク(アドレス)と区別が付けば何でも良いことなのですが、ポインタに入る値で明らかに通常のアドレスとして扱われない値として一般的なのが NULL なので、特別な理由が無い限り NULL が使われているだけなのです。

これと同じ要領で、新たに入力した要素を最後尾に持っていくためには、ダミーデータとして置いているlistHead.nextをNULLとして置くのではないかと考えています。

上に書きました通り、最後尾の next は NULL にするのであって、 next を NULL にすれば(何かしらの力で ?) それまで追加したデータの最後尾に行く訳ではありません。
listHead の next をNULL にしたら listHead が最後尾として扱われ、つまりリストのデータが何も無かった最初の状態になるだけです。(ついでにそれまで malloc() されたメモリが放置されメモリリークとなります。)
変数への代入は、ただ値を入れるという処理だけです。その変数が変わることで処理が変わるようなプログラムを書かない限り、何も起きません。

投稿2022/12/05 13:27

編集2022/12/05 19:31
jimbe

総合スコア12512

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

homiru

2022/12/06 04:58

すみません、逆順と正順を逆に考えていました。 誤解を生んでしまい本当に申し訳ないです。 僕のプログラムは、pInsはループ内で常にlistHeadを指していました。 正順だと常にループで変わっていくことを見落としていました。 このような場合(今回のプログラムに限らず)、常にループで変わっていくものは、ループから外して書かないといけない、ということですね? 1をやった後、2をやらなければ逆順(つまり僕が作成したプログラムの結果)になり、2をやれば正順になる、実際絵でかいてみると本当にその通りでした。 pIns->next=pNew; このままだとpIns = list head,その次(pins->next)がpNew...と永遠に逆順に繋がっていくだけでした。うまく文章にして伝えることができませんが、pIns=pNewにすることでループする際、新たに入力されたpNewの次が、先ほど入力されたpNewの次につながる、ということでしょうか? 上手く文章にして伝えられなくて本当に申し訳ないです。 jimbe様の説明、凄くわかりやすかったです。 nextとNULLの関係性をしっかり理解できていませんでした。 理解していたつもりだったのですが、出来ていなかったです。 再度勉強し直します。 最後尾の次をNULLとする、と定義しているのであって、NULLだから最後尾ではない、しっかり覚えておきます。 jimbe様から見て、僕のプログラムは不完全極まりないと思うのですが、どのように改善すればより良いプログラムになると思いますか? 初心者なので、あまり複雑で凝ったものは作れないのですが、自分の作ったプログラムに愛着を持ってプログラミングをしているので、そのプログラムがさらにグレードアップできるのなら、挑戦してみたいです。 こんなに丁寧に質問に答えて頂き、本当にありがとうございます。
jimbe

2022/12/06 09:41 編集

>pInsはループ内で常にlistHeadを指していました。 >正順だと常にループで変わっていくことを見落としていました。 >このような場合(今回のプログラムに限らず)、常にループで変わっていくものは、ループから外して書かないといけない、ということですね? どのような状況に対応する為にどのような変数を用意し何時どのタイミングでどう変化させるのかは、プログラムを作る上で常に意識しなければならない重要なことです。 コードの pIns のコメントには >List *pIns; /*要素の挿入場所になる要素のポインタ*/ と書かれています。 pIns をこのように使うと決めたのでしたら、正順時は常に listHead を指す必要があるので、実際に処理をするまでに pIns=&listHead が実行され、かつ以降変わらなければ良いことになります。 そして逆順時は、 pIns は追加される度にその追加されたノードを指す必要がある(そうで無ければコメントに書いたのは嘘になる)ので、ループの中で pIns=&listHead をやるわけにはいかない、ということになります。 >実際絵でかいてみると リスト等のポインタ操作は何処まで絵的にイメージ出来るかが理解の助けになると思いますので、それが出来ているなら良い感じでは無いでしょうか。 本件は next だけですが、 前を指す prev のある双方向とか、先頭と最後が繋がったリング状とかになると慣れないと頭の中だけでは難しいですので。 >僕のプログラムは不完全極まりないと思う いえ、逆順としては(回答の点以外は)ほぼ完全だと思います。 リストの構造・操作のイメージが出来れば後はリストのライブラリ的にどう関数を分けるかとかどんな機能を付けるか(『追加、削除、探索等のリストならではのもの』は作るつもりのようですが)になってくるので、リストそのもののコードというよりはリストを利用する側に提供することも考えることになってくると思います。
guest

0

pNew を書き込むべき場所を pIns が持てばいいでしょう。

C

1#include <stdio.h> 2#include <stdlib.h> 3 4typedef struct list { 5 int element; 6 struct list *next; 7} List; 8 9void showList(List *p) 10{ 11 for (; p; p = p->next) printf("element:%d\n", p->element); 12} 13 14int main(void) 15{ 16 List *head = NULL, **pIns = &head; 17 for (int i = 0; i < 5; i++) { 18 List *pNew = malloc(sizeof(List)); 19 printf("input data: "); 20 scanf("%d", &pNew->element); 21 pNew->next = NULL; 22 *pIns = pNew; 23 pIns = &pNew->next; 24 puts("=================="); 25 showList(head); 26 puts("=================="); 27 } 28}

投稿2022/12/05 16:29

kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

C

1#include <stdio.h> 2#include <stdlib.h> 3 4struct list{ 5 int element; 6 struct list *next; 7}; 8 9typedef struct list List; 10 11void showList(List *pHead){ 12 List *p; 13 p=pHead; 14 while(p!=NULL){ 15 printf("element:%d\n",p->element); 16 p=p->next; 17 } 18 return ; 19} 20 21/* headを先頭とする単方向リストの末尾に要素を追加し、 22 リストの先頭を返す */ 23List* appendList(List* head, int elm) { 24 List* node = (List*)malloc(sizeof(List)); 25 if ( node ) { 26 node->element = elm; 27 node->next = NULL; 28 List* prev = NULL; 29 List* p = head; 30 while ( p != NULL ) { 31 prev = p; 32 p = p->next; 33 } 34 /* この時点で prev->next == NULL、つまり prev はリストの末端を指す */ 35 if ( prev == NULL ) head = node; 36 else prev->next = node; 37 } 38 return head; 39} 40 41int main(void){ 42 List* head = NULL; 43 for ( int i = 0; i < 5; i++ ) { 44 head = appendList(head, i); 45 } 46 /* できたかな? */ 47 showList(head); 48 return 0; 49}

※ 要素数が増えるにしたがって末尾へのポインタ移動に時間かかっちゃうから、フツーはもちっとマシな実装するんだけども...

投稿2022/12/05 06:51

編集2022/12/05 12:52
episteme

総合スコア16614

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

homiru

2022/12/06 04:20

すみません、まだリストについてしっかりと分かっていないので、まずは簡単なところから進めようとしています。 僕は、端から見たら凄くおかしなプログラムを作ってしまったのでしょうか? 本当にすみません、勉強しなおします。 この場合、どのようなプログラムを組むのが正解なのでしょうか。 初心者故に、浅い知識の詰め合わせでしか作ることができなくて...。 こんな初心者同然のプログラムに対し、丁寧に回答してくださりありがとうございます。 これからも頑張って、プログラミングしていきます。
episteme

2022/12/06 05:25

> 僕は、端から見たら凄くおかしなプログラムを作ってしまったのでしょうか? そんなことはないと思う。 showListはListのナカミをずらりとプリントする関数だけど、 これに限らず: - 要素を先頭に追加 - 先頭の要素を削除 - 要素を末尾に追加 - 要素数を返す - ナカミを空にする などなどListに必要な操作がいくつかあるハズ。 これらを(必要となったとき)いちいちベタ書きするより(ここではappendListのように)関数に仕立てた方がいいんじゃない? って思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問