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

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

ただいまの
回答率

87.58%

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

解決済

回答 6

投稿

  • 評価
  • クリップ 0
  • VIEW 1,207
退会済みユーザー

退会済みユーザー

コードのどこで実行結果の「ファイルから読んだ文字列: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
$
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 6

+2

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/21 13:54

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

    キャンセル

  • 2018/01/21 14:00

    紙と鉛筆で十分じゃないですか?
    自分で書くというのが大事なところです。

    キャンセル

  • 2018/01/21 14:04

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

    キャンセル

+2

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

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

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

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

    キャンセル

  • 2018/01/23 18:49 編集

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

    キャンセル

  • 2018/01/23 19:06

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

    キャンセル

checkベストアンサー

+1

コードのどこで実行結果の「ファイルから読んだ文字列: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 16:03

    そういうことです。

    キャンセル

  • 2018/01/21 17:41

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

    キャンセル

  • 2018/01/21 17:47

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

    キャンセル

0

???

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

みたいな文字列は

main関数ででしょ。

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/21 13:19

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

    キャンセル

  • 2018/01/21 13:43

    疲れているときは私もそうです。
    専門としてのもの ( 職業としてのプログラマならプログラミングとか ) でも、疲れていると厳しいです。

    まぁ、それは置いといて、

    TaroToyotomiさんが言うように、「一行ごとにコメントを記述してみる」でしょうかね。

    一気にやろうとすると厳しいので、一行ごとに「この行は何をしているのか」をコメントとして残して、

    流れを見る。

    すると理解しやすくなると思います。

    私も、「データ構造とアルゴリズム」を学ぶときはそういう風にしていました。

    キャンセル

  • 2018/01/21 14:03

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

    キャンセル

0

struct address{
      char name[N];
      char address[N];
      char tel[N];     // 電話番号
      char mail[N];
      struct address *next;
      struct address *before; 
};

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

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

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

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

digraph G {
  A -> B
  B -> A
  B -> C
  C -> B
  C -> D
  D -> C
  "current watching" -> B
}

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

dot

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

http://www.webgraphviz.com/

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/23 18:31

    すみません。http://www.webgraphviz.com/に行って、図をかけることが分かりました。
    テンプレートを使って自分の図を作るには、どのようにするのですか。それをteratailの質問やコメントに貼り付け方を、教えて頂けますか。

    キャンセル

  • 2018/01/23 19:38

    私は面倒だったのでそのサイトでできたやつをスクリーンショット取って貼り付けてしまいましたが、本家本元のgraphvizを使って画像にしてもいいと思います
    https://qiita.com/yasuto777/items/c1c4e583f393b27ebb7a

    キャンセル

  • 2018/01/23 20:34

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

    キャンセル

0

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 87.58%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

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