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

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

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

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

Q&A

解決済

6回答

1769閲覧

コードのどこで実行結果の「ファイルから読んだ文字列:hosi,nagoya,5436,mail-7」を表示しているんですか

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

0グッド

0クリップ

投稿2018/01/21 03:15

コードのどこで実行結果の「ファイルから読んだ文字列:hosi,nagoya,5436,mail-7」
を表示しているんですか。
それと何度も教えてもらっているのですが、関数void list_add()の下のほうの
p->next = *head;
if (p->next != 0)
p->next->before = p;
p->before = 0;
*head = p;    
を詳しく教えていただきませんか。分からないのは引数がstruct address **headになっているためだと思います。
*headでポインタを表わしているのは分かるのですが、struct address **headの中には
char *nameが含まれているので、struct address **headがポインタのポインタになっていると思うんですが。そのあたりも含めて、上のコードを説明していただけませんか。

コード #define N 256 #define FILENAME "address.csv" struct address{ char name[N]; char address[N]; char tel[N]; // 電話番号 char mail[N]; struct address *next; struct address *before; }; void chop(char *p) { for (; *p; p++) ; p--; // *pが\0となりfor()を抜けるとここに来る。 //ポインタを1個戻して,\0 の前が'\r'か'\n'どうか調べるため。 while (*p == '\r' || *p == '\n') *(p--) = 0; }//chop()を抜けると\0で区切られた文字列になる。 void list_add(struct address **head, char *name, char *address, char *tel, char *mail) { struct address *p; if ((p = malloc(sizeof(struct address))) != 0) { strcpy(p->name, name); strcpy(p->address, address); strcpy(p->tel, tel); strcpy(p->mail, mail); p->next = *head; //**headでもらっているので*headは構造体の(head)アドレスである if (p->next != 0) p->next->before = p; //(p->next)->before = p //(p->next)の中のbeforeはpを指している p->before = 0; //こんがらがっています *head = p; //新しく構造体の(head)アドレスをpにする //こんがらがっています } } int main() { struct address *head; FILE* fp; static char buff[N], name[N], address[N], tel[N], mail[N]; char *token=","; head = 0; if ((fp = fopen(FILENAME,"r")) != 0) { while(fgets(buff, N, fp) != 0){ //本当の大元の文字列を書き換えないようにするために //bufを確保してコピーし、それをstrtok()の引数にしている。 char *p; chop(buff); printf( "ファイルから読んだ文字列:%s\n", buff ); p = strtok(buff, token); if ( p != NULL ) { strcpy(name, p); } else { printf( "氏名の切り出しに失敗しました。\n"); break; } p = strtok(NULL, token); if ( p != NULL ) { strcpy(address, p); } else { printf( "住所の切り出しに失敗しました。\n"); break; } p = strtok(NULL, token); if ( p != NULL ) { strcpy(tel, p); } else { printf( "電話番号の切り出しに失敗しました。\n"); break; } p = strtok(NULL, token); if ( p != NULL ) { strcpy(mail, p); } else { printf( "メールアドレスの切り出しに失敗しました。\n"); break; } list_add(&head, name, address, tel, mail); } } fclose(fp); return 0; } 実行結果 naka@naka ~ $ cd kadai/kadai9-8 naka@naka ~/kadai/kadai9-8 $ gcc -o kad9-8a kad9-8a.c -Wall naka@naka ~/kadai/kadai9-8 $ kad9-8a ファイルから読んだ文字列:hosi,nagoya,5436,mail-7 ファイルから読んだ文字列:kato,kanagawa,080-8888,mail1-2 ファイルから読んだ文字列:koko,yosida,090-2314,mail-6 ファイルから読んだ文字列:naka,kamikosaka,080-4444,mail1-1 ファイルから読んだ文字列:nasi,nogata,090-6376,mail-8 ファイルから読んだ文字列:saito,yamanashi,080-6666,mail1-3 ファイルから読んだ文字列:sato,tokyo ,090-3333,mail1-4 ファイルから読んだ文字列:suzuki,saitama,090-2222,mail1-5 nakamura@nakamura ~/kadai/kadai9-8 $

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

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

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

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

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

guest

回答6

0

こういうものはプログラムの字面や文章の説明だけを見ていても埒があかない・隔靴掻痒・百聞は一見にしかずというもので、TaroToyotomiさんが仰る通り、図に描く、それも手書きで十分、そうすれば自ずと見えてくるものです。
でも、そもそもどんな図を描けばよいのかが分からない、って感じですよね。仕事からもどっても相変わらずの様子なので、取り急ぎ描いてみました。

まずlist_add()関数の、新しく構造体を追加する部分のコードだけを残すと、こうなる。

C

1void list_add(struct address **head, /*他の引数は省略*/) 2{ 3 struct address *p = malloc(sizeof(struct address)); 4 // 以下、「自分」とは p が指す、獲得したばかりの構造体 5 // (図1の時点) 6 // **headでもらった*headは、リストの先頭(現在先頭)を指している 7 p->next = *head; // ①「現在先頭」を、自分のnextが指す 8 if (p->next != 0) 9 (p->next)->before = p; // ②「現在先頭」のbeforeは自分を指す 10 p->before = 0; // ③自分の before は無いのでNULLを代入 11 *head = p; // ④新たに自分が先頭になる 12 // (図2の時点)

今、双方向リストに2つ以上の構造体がつながっている、即ち
・「head <=> A <=> B ...」というリストになっている
・2つの構造体のアドレスは A, B
と仮定します。

ここで、list_add()関数が malloc() して、新たな構造体となるメモリがアドレスPから獲得できたとする(ポインタpに P というアドレスが返った)。その時点の状態が図1です。

図1
図1

念の為、補足。
・「現在先頭」とは A のこと
・list_add()関数の引数 **head は、main()関数の *head ポインタを指している
・malloc() した時点で、P番地の構造体の *next, *before は不定だから「?」
ここまで、良いですか?

さて、Pをリストの先頭に挿入するとは、どういうことか。
「head <=> P <=> A <=> B ...」というリストになれば良い。その結果が図2です。

図2
図2

つまり、図1の状態から図2の状態に変えるためのコードが、list_add()の5行。やってることはポインタの付け替えです。
上のコードのコメントに①〜④をつけました。それに対応して「代入された場所」にも図2に赤字で①〜④をつけてあります。2つの図と、コードと、コメントを見比べてみてください。
どうですか。デバッガで追うという話も出てますが、それよりもこんな図を描いたほうが遥かに分かりやすいと思いますよ。

最後に、私から質問。if (p->next != 0)という条件は何を意味するのか、どんな場合なのか、わかりますか?

投稿2018/01/21 15:56

rubato6809

総合スコア1380

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

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

退会済みユーザー

退会済みユーザー

2018/01/23 08:44

ありがとございます。大変丁寧に説明と図を頂き非常に助かりました。 何回も読み直してやっとわかりました。すっきりしています。 リスト構造の電話帳のプログラムをよんでいます。その中の関数でした。 「どんな図を描けばよいのかが分からない」おしゃるとおりでした。 次に進みます。また分からないところがあったら、教えてください。
退会済みユーザー

退会済みユーザー

2018/01/23 09:00

*headがNULLでないということですか。*headがすくなくともデータをさしているということだとおもいますが、あっていますか。
rubato6809

2018/01/23 09:03

逆に、*headがNULLとは、どういうこと?
退会済みユーザー

退会済みユーザー

2018/01/23 09:05

*headがNULLのときは *head=p;でp->next=0;p->before=0;でいいでしょうか
rubato6809

2018/01/23 09:07

で、*headがNULLの場合とは、要するに、どんな状態ですか?図に描けますか?
退会済みユーザー

退会済みユーザー

2018/01/23 09:11

if(p->next != 0)はif(p->next =1)で「現在先頭」のアドレスが真だからデータAがあるということですか。
退会済みユーザー

退会済みユーザー

2018/01/23 09:15

説明の図は読めるんですが、コメントに図を載せるやり方がわからないのですが、 教えてもらえますか。
rubato6809

2018/01/23 09:16

*headが0でない場合は、Aがある、Aがつながってるということで正しいですよ。 でも答えてほしいのは、それじゃないんです。 もう一度ききます。 *headがNULL(或いは、*headが0)の場合とは、どんな状態ですか?
退会済みユーザー

退会済みユーザー

2018/01/23 09:17

*headが何も指してないですか。
rubato6809

2018/01/23 09:37 編集

そうです。 リストには何もつながっていない状態。AもBもつながっていない状態。プログラムが走り始めた時、何もつながっていない状態で、今からPが初めてリストにつながろうとしている時です。 その場合、②の場所(*beforeポインタ)にPのアドレスを書こうにも、書く場所が無いんですよ。なので、この場合、(p->next)->before = p; を実行できず、if文でスキップするというわけ(無理に実行すればメモリアクセス例外などが起こる)。 条件が合うから実行する、ばかりがif文の使い方ではなくて、条件が合わない場合は実行しない、そこにも思いを巡らすことが大事。 よって、リストに最初に追加する場合と、二回目以降では、②を実行するかしないかが違う。 これも図に描いてみてください。貴方がご自分でトライして、できれば結構。
退会済みユーザー

退会済みユーザー

2018/01/23 09:41

ありがとうございます。お手数おかけします。 説明していただいた図1、図2、を自分のコードの中にコピーしたいのですが、どうすればコピーできますか。図をコメントにのせるのはどうしてやるのでしょうか。教えてくださいお願いいたします。
rubato6809

2018/01/23 09:49 編集

仕様書だったらWordファイルなどで作るから、図を載せることもあるけど、 プログラムは文字だけで書くテキストファイルなので、図を含めることはしないし、できないよ。アスキーアートでコメントとして図を描く強者はいるかもしれないけどね。
退会済みユーザー

退会済みユーザー

2018/01/23 10:06

rubato6809さんに書いて頂いた図1、図2を自分のコードのコメントに保存したいのですけど、 コピーすると図1、図2の箇所がでていないのですが、ほうほうありますか。
guest

0

struct address構造体の中身を図に表現することはできますか?
コードで行っている操作を1行づつ追いかけて図に記入していけば何をしているのか自ずと理解できると思います。

投稿2018/01/21 04:31

TaroToyotomi

総合スコア1430

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

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

退会済みユーザー

退会済みユーザー

2018/01/21 04:54

ありがとうございます。 図に表現することが出来ていれば、理解できていると思うのですが、 私のパソコンでは図を描けないので、添付できません。おしゃるとおりですね。図で説明しているリストで理解できたこともあるのですが、表現が少し変わると、わからなくなります。デバッグでstepやってみます。
TaroToyotomi

2018/01/21 05:00

紙と鉛筆で十分じゃないですか? 自分で書くというのが大事なところです。
退会済みユーザー

退会済みユーザー

2018/01/21 05:04

すみません。やってみます。
guest

0

ベストアンサー

コードのどこで実行結果の「ファイルから読んだ文字列:hosi,nagoya,5436,mail-7」

を表示しているんですか。

つprintf( "ファイルから読んだ文字列:%s\n", buff );

list_add()は、addressの双方向リスト先頭に新規にaddressを追加していますが、リストの先頭のポインタを呼び出し元に返すため、ポインタのポインタになっています。

追記
p->next = *head; # 新しく確保したaddressの次ポインタに、リストの先頭を代入する。
if (p->next != 0) p->next->before = p; # リストの先頭の前ポインタに、新しく確保したaddressを代入する。
p->before = 0; # 新しく確保したaddressの前ポインタに無し(0)を代入する。
*head = p; 新しく確保したaddressをリストの先頭として、呼び出し元に返す。

投稿2018/01/21 03:36

編集2018/01/21 04:19
hichon

総合スコア5737

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

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

退会済みユーザー

退会済みユーザー

2018/01/21 03:46

おせわになっています。list_add()の下のコードを p->next = *head; から詳しく優しい表現で 説明していただけませんか。list関数を朝から復習していました。いつもこの辺でひっかかります。 図解されているネットで、よくわかることもあるんですが、表現が少し違うと分からなくなります。 よろしくお願いいたします。
hichon

2018/01/21 04:21

訂正&追記しました。リンク先の双方向リストの説明がわかれば、そんなに難しくありません。
退会済みユーザー

退会済みユーザー

2018/01/21 04:48

ありがとうございます。 p->next = *head;はp->next が*headを指していて、新しく確保したアドレスにデータを追加するということですよね。 p->before = 0; # 新しく確保したaddressの前ポインタに無し(0)を代入する。というのはpを*headにするということですか。 if (p->next != 0)で、p->next は*headを指しているんですよね。 if()文はif (*head != 0)ということですよね。 pのp->next が*headを指しているんで、*head->beforeはpとなるんですね。 ようするに今度はpを*headにして返すということでいいでしょうか。
hichon

2018/01/21 07:03

そういうことです。
退会済みユーザー

退会済みユーザー

2018/01/21 08:41

私のコメントに間違いはありませんか。 あったら指摘してほしいのですが。 ありがとうございます。
退会済みユーザー

退会済みユーザー

2018/01/21 08:47

コードをgdbでstepしながら、ポインタや値などをprintoutしながら確かめてみています。500行近くになりました。今日は時間が無くなりましたので、明日にでも整理しながらやろうと思います。 ありがとうございました。
guest

0

質問の回答の本題と外れますが、質問一覧を見て気になったので回答します。

1,まず、gccでコンパイルするのは良いのですが、ステップ実行で変数の内容を確認(ウォッチ)できて、デバック実行のできる無料のIDE(統合開発環境)を導入してみてはどうでしょうか?

2,学習目的ならよいのですが、一般的にリストを作成したい時はコレクションフレームワーク c++ならstl(Standard Template Library )のlistなどを使用すると思います。できるだけ自作はバグの発生元なので「学習目的」以外は避けたほうが無難です。

投稿2018/01/21 10:02

umyu

総合スコア5846

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

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

0

c

1struct address{ 2 char name[N]; 3 char address[N]; 4 char tel[N]; // 電話番号 5 char mail[N]; 6 struct address *next; 7 struct address *before; 8};

ということはまず双方向リストですね?(nextbeforeがあるので)

するとこの構造体はNodeです。

操作を理解するにあたっては、すべてのnodeがどうつながっているかを各処理ごとに整理するとつながりの変化がわかり、いいと思います。

あとこの手のを図式化するには、もしかしたらgraphviz(dot)をつかうといいかもしれません。

dot

1digraph G { 2 A -> B 3 B -> A 4 B -> C 5 C -> B 6 C -> D 7 D -> C 8 "current watching" -> B 9}

のようにNode間のつながり書くと

dot

のように簡単に図がかけます

http://www.webgraphviz.com/

投稿2018/01/21 05:26

yumetodo

総合スコア5850

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

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

退会済みユーザー

退会済みユーザー

2018/01/21 08:39

ありがとうございます。リスト構造の住所録のプログラムを勉強中です。 間口が広がっていっこうに進みません。URLをせっかく教えて頂いたのですが、後日ちょうせんしたいとおもいます。英語は苦手です。
yumetodo

2018/01/21 08:52

ああ、貼ったURLはGraphviz入れなくてもWeb上で図がかけますよ、という意味合いのものでした。テキストボックスに書いて、「Generate Graph」で図ができます。
退会済みユーザー

退会済みユーザー

2018/01/21 09:13

URLに行ってdownloadしたんですがやり方が分からないので どうして図を描くのか教えていただけますか。 図を書きたいときがあるので、おねがいします。
yumetodo

2018/01/21 10:16

>URLに行ってdownloadしたんですが 一体何をDLしたんだ・・・なにもDLする必要はないのに・・・。新年早々怪談かな?
退会済みユーザー

退会済みユーザー

2018/01/21 10:30

ありがとうございます。あしたまたやります。
退会済みユーザー

退会済みユーザー

2018/01/23 11:34

ありがとうございます。やってみます。
guest

0

???

ファイルから読んだ文字列:hosi,nagoya,5436,mail-7

みたいな文字列は

main関数ででしょ。

それともなぜこのコード ( 特に 構造体のnext, beforeに付け替えたりするだけで可能なのか等 ) ということでしょうか?

投稿2018/01/21 03:28

BeatStar

総合スコア4958

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

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

退会済みユーザー

退会済みユーザー

2018/01/21 03:37

すみません printf( "ファイルから読んだ文字列:%s\n", buff );を見落として、「ファイルから読んだ文字列:」のところだけ見ていました。 ありがとうございます。
退会済みユーザー

退会済みユーザー

2018/01/21 03:48

後の質問はコードがやっていることが分からないということです。
BeatStar

2018/01/21 04:08

ソースコードの意図というか意味が理解できないと... では、どこが理解できないかわかりますか? たとえば、list_add関数内だと strcpyを使っている範囲は理解できるが、 if (p->next != 0)あたりがわからんとか。 もし、それで2種類のイメージがあるのなら、それも提示したほうがいいかも。 たとえば、 「ポインタがないから別のものに付け替えているのか、ポインタだからそういう処理になっているのかわかりません...」とか。 自分が思ったことを記述すると質問者は回答しやすい場合があります。 もしかしたら「惜しい」レベルかもしれないし、「真逆の理解をしている」かもしれない。 あるいは見落としによるものかもしれない。 そういう情報があることで、イメージしやすくなりますから。
退会済みユーザー

退会済みユーザー

2018/01/21 04:19

void list_add()関数の下のほうのコードです。質問のところに書いているコードなんですけど、 リストのadd()関数は結構見ているんですが、疲れていたりすると、こんがります。分かってないんですね。 似たような質問をしているので、いつも回答してくださる方も少なくなってきました。 休憩してリストを復習してみます。ありがとうございます。
BeatStar

2018/01/21 04:43

疲れているときは私もそうです。 専門としてのもの ( 職業としてのプログラマならプログラミングとか ) でも、疲れていると厳しいです。 まぁ、それは置いといて、 TaroToyotomiさんが言うように、「一行ごとにコメントを記述してみる」でしょうかね。 一気にやろうとすると厳しいので、一行ごとに「この行は何をしているのか」をコメントとして残して、 流れを見る。 すると理解しやすくなると思います。 私も、「データ構造とアルゴリズム」を学ぶときはそういう風にしていました。
退会済みユーザー

退会済みユーザー

2018/01/21 05:03

いろいろご助言ありがとうございます。 自分のコードには、わからないところには詳しくコメントしています。 コメントしているうちにこんがらがった時に、質問させてもらっています。 単方向リストではこのようなコードの詳しい説明をしてもらって、わかったつもりでいたんですが、よく理解できていなようです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問