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

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

ただいまの
回答率

87.58%

if()文の条件を図にしています。矢印の方向がわかりません。

解決済

回答 5

投稿 編集

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

退会済みユーザー

void exchange() の中にif (&((*p)->next) == q があります。

イメージ説明
(*p)->next)はポインタpのnextでそのnextに入っている(*p)->next)のアドレスがポインタqのアドレス
と等しいということですよね。
上図でいいでしょうか。
よろしくお願いいたします。

#include <stdlib.h>
#include <string.h>
#include <assert.h> //

#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 data_show(struct address* head);
void data_sort(struct address** head);

void chop(char *p){
  for (; *p; p++)
    ;
  p--; 

  while (*p == '\r' || *p == '\n')
    *(p--) = 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;
          if (p->next != 0)
              p->next->before = p;
              p->before = 0;
              *head = p;
      }
}

void data_show(struct address *head) {
  if (head != 0) {
    printf(" %s, %s, %s, %s\n", head->name, head->address, head->tel, head->mail);
    data_show(head->next);
  }
}

struct address **listmin(struct address **head) {
  struct address **p;

  if (*head == 0)
    return 0;
  if ((*head)->next == 0)
    return head;
    //

  if (strcmp((*head)->name, (*(p = listmin(&((*head)->next))))->name) < 0)
    return head;
  else
    return p;
}

void exchange(struct address **p, struct address **q) 
{![![イメージ説明](993302354d80fe7a9b8e9f78d293b8cc.jpeg)](b2aa359925707f49b302e1bc144a7110.jpeg)
      struct address *r, *s, *t;
      assert(*p != 0 && *q != 0);

      if (p == q) return;

      r = *p; s = *q;
      if (&((*p)->next) == q ) {
                if (s->next != 0){ 
                s->next->before = r;
             }
             s->before = r->before;
             r->before = s;

             *p = s;
             *q = s->next;
             s->next = r;

             return;
      }else{
          //(&((*p)->next) == q) && (&((*q)->next) == p)

            if (s->next != 0) s->next->before = r;
            t = s->before;
            s->before = r->before;

            if (r->next != 0) r->next->before = s;
            r->before = t;
            t = r->next;
            r->next = s->next;

            s->next = t;

            *p = s;
            *q = r;
      }
}

void data_sort(struct address **head) {
      struct address **p;

      if (*head != 0) {
          for (;;) {
              p = listmin(head);

              if (p == 0)break;

           exchange(head, p);


              head = &((*head)->next);

             exchange(head, p);
             head = &((*head)->next);
       }
     }
}

void release(struct address **head) 
{
      if (*head != 0) {
        release( &((*head)->next) );
        free(*head);
        *head = 0;
      }
}

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);
      }  
      data_sort(&head);
      data_show(head);

      release(&head);
      return 0;
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • 退会済みユーザー

    退会済みユーザー

    2018/01/31 19:16

    コードの内容が分からないので質問しています。課題には回答はついていませんので、自分で参考になるプログラムを見つけて、答えをつくっています。課題の丸投げではありませんので、あしからず。

    キャンセル

  • 退会済みユーザー

    退会済みユーザー

    2018/01/31 19:19

    回答いただいた方に再度回答をしてもらえるよう依頼しています。

    キャンセル

  • rubato6809

    2018/01/31 19:30

    「新版明解C言語中級編の9章の自由課題8の復習て渋滞中」・・・これが電話帳プログラムの課題ですか?

    キャンセル

回答 5

+2

解決済みだけど質問者はモヤモヤのままでしょう。正解を示さないままでは私も引っかかりが残るので図をアップします。デジタルで矢印を上手く描けなかった(苦笑)ので矢印を手書きしました(他に誰も図を描かないのか?という思いもある)。

まず次の部分から確認します。バグ修正と説明の都合でコードを一部変更・省略しています。

void exchange(struct address **p, struct address **q) 
{
    struct address *r, *s, *t;

    r = *p; s = *q;
    if (&((*p)->next) == q) {    // こちらの条件だけで十分
        if (s->next != 0) {
            s->next->before = r;
        }
        s->before = r->before;   // バグ修正
        r->before = s;
        *p = s;
        *q = s->next;
        s->next = r;
        return;


r, s ポインタに値を代入し、if 文の条件が成り立った時点が図1です。
図1
図1

X番地、Y番地、Z番地にある3つの構造体が X ⇒ Y ⇒ Z というつながりになっている状態です。構造体の先頭アドレスに加えて、構造体中の next, before ポインタのアドレスを XN, XB, YN, YB ... としました。p ポインタの値は XN であり、XN は(Xの)next ポインタのアドレスであって、next ポインタの値ではない事を念押しします。
p の値は next ポインタのアドレスである、即ち next ポインタをポイントしている。

全てのデータはメモリ上にある=全てのデータにメモリアドレスがある。ポインタが活躍するプログラムを読み書きする時、メモリの値なのか、メモリのアドレスなのか、きちんと把握することは大事です。だから図を描いてみると良いのです。

さて、exchange() の2つの引数 p, q はポインタのポインタである:

  • struct address **p は X構造体の next ポインタ(XN番地)を指す
  • struct address **q は Y構造体の next ポインタ(YN番地)を指す

さらに、その2つの next ポインタの値を代入した r と s が指している Y と Z が交換対象である。交換しようとしているのが Y と Z である事と、p, q が指している場所を、勘違いしないようにしましょう。
つまり  if (&((*p)->next) == q)  の条件は隣り合う2つを交換する場合を意味します。ここ、問題ありませんよね?・・・と書いたら問題ありまくり、というかここが質問のキモでしたね。ポインタ、*演算子、&演算子などが苦手な人はひっかかるでしょう。図1を見ながら解説を編集します。

 &((*p)->next) の値は YN という番地です。Y番地の構造体にある、nextポインタ自体のメモリアドレスです。q ポインタの値も YN です。だから一致するのです。これに対し質問者は

&((*p)->next) の値は…XN番地ではないのですか。YN番地でいいのでしょうか。

と問い返しました。図1に next ポインタはXN番地、YN番地、ZN番地の3つありますが、XN番地の next を ((*p)->next) だと勘違いしたのです。こういう時は気を落ち着けて  struct address ** p;  を、こんな風に整理すると良いと思います。

  • p の値は XN である(XN番地の next ポインタを指す)
  • *p の値は Y である(Y番地の構造体を指す)
  • **p の値(?)は Y番地にある構造体である

p, *p, **p と、参照演算子「*」の数が増えるにつれて図1の中の注目点が移っていきます。*p == Y を確認できれば  (*p)->next  を  Y->next  と置き換えることができ、  (*p)->next  は Y 番地の構造体の中の next ポインタだと理解できるはずです。

質問の次の部分はツッコミどころでした。

(*p)->next)はポインタpのnextでそのnextに入っているアドレスがポインタqのアドレスと等しい

そもそも、next ポインタは構造体の先頭をポイントするのに対し、p, q の値は next ポインタ(構造体の途中)をポイントする(=構造体の先頭をポイントしない)のだから、「next入っているアドレスがポインタqのアドレスと等しい」とはなりません。

よく見ると「(*p)->next)はポインタpのnext」が上記の勘違いを示しているし、「nextに入っているアドレスがポインタqのアドレスと等しい」にも混乱が見えます。私的には「ポインタqのアドレスと等しい」という表現にダメ出しをしたい。ここは「ポインタ q の値」としなくてはならない。

「いや、q のアドレスとは(qは値としてアドレスを持つポインタなのだから)q がポイントしている場所(== YN)という意味だ、だからqの値のことだ」という弁解が予想できますが、「qのアドレス」と言ったら変数 q 自身のメモリアドレスを意味します(複数の解釈ができるとしても、第一の意味はこれ)。図1には q 自身の番地を示していないが、もちろん q 自身にもアドレスはあります。ただ、コードを解釈するのに q のアドレスを考える必要がないので省略したまで、「qのアドレス」という表現は無用=誤りだったのです。

繰り返しますが、コードを読み書きする時、変数の値なのか、変数のアドレスなのか、明確に区別することが大事です。区別が曖昧だとあんな表現をしてしまいがち。そして、こんなにもメモリアドレスを意識しなければならないのは、アセンブリ言語を除けばC/C++ぐらいしか無く、ポインタが苦手な人が多い要因でもあるでしょう。その苦手を克服するには、変数領域のイメージを図にしてみる、変数の具体的なアドレスを確認する、を繰り返すことだと思います。メモリダンプできると、なお良いと思う。こうしたことがポインタ廻りの文法を自然に理解する近道であり、手間を惜しむな、と言いたい。質問者はコードを理解しようと膨大なコメントを書き込んでいましたが、言葉・文章よりも図のほうが雄弁です。それにこの間のやり取りは図を使うと議論が明確になることを実証したのじゃないですか(笑)。

さて、図1の関係が成り立つ時、X ⇒ Y ⇒ Z というつながりを、X ⇒ Z ⇒ Y というつながりに変えようとします。各変数の値・各ポインタの値がそれぞれどうなればよいか、考えてみてください。


次に、 if (&((*p)->next) == q)  が成り立たない場合とは、離れた構造体を交換する場合です。
操作対象の構造体がひとつあると、その前後の構造体の next, before が変更を受けるので、都合3つの構造体に影響します。exchange()は2つを交換するから、最大で6個の構造体が影響を受けるという事。その6つを A ⇒ B ⇒ C ⇒ D ⇒ E ⇒ F とします。交換対象は B と E です。p, q, r, s の各ポインタがどこをポイントしているか、ご確認ください。
図2

図2,6つの構造体

B と E を交換したら、どうなるか。
A ⇒ B ⇒ C ⇒ D ⇒ E ⇒ F を A ⇒ E ⇒ C ⇒ D ⇒ B ⇒ F というつながりにすることが目的ですが、リスト構造ではデータそのもの(構造体、その中の name[], address[]など)を動かさず、ポインタを付け替えて順番を変えるのです。次は交換後の図です。
図3

図3、交換後

例えば、A番地の構造体のnextポインタ(AN番地)が「B→E」とあるのは、この操作でnextポインタの値がBからEに変更される事を表します。
この、ポインタの付け替えをしているのが else 以下のコードです。

    r = *p; s = *q;
    if (&((*p)->next) == q) {
        // 隣り合う2つを交換する(省略)
    } else {
        // 離れた2つを交換する
        if (s->next != 0) {
            s->next->before = r;
        }
        if (r->next != 0) {
            r->next->before = s;
        }

        // 2つの before ポインタを交換する
        t = s->before;
        s->before = r->before;
        r->before = t;

        // 2つの next ポインタを交換する
        t = r->next;
        r->next = s->next;
        s->next = t;

        *p = s;
        *q = r;
    }


t ポインタを使った3行のパターンが2つあります。これは X と Y を交換する時、一時変数 t を使って

    t = X;
    X = Y;
    Y = t;


とするのと同じです。図3で言えば B と E の next, before 2つの交換です。他にも同じパターンで交換できる所がありそうですが、
ともかく全部で8本のポインタを書き換えるべきことが図から分かりますし、コード上にもポインタの値を書き換えるところが8行あります。質問者は自ら手を動かして、その8箇所について、図とコードの対応をとってみると良いと思う。


P.S.1
参考にしたコードにあった決定的なバグは、次に示した最後2行の順番が逆になっていることです。

    if (&((*p)->next) == q) {
        if (s->next != 0) s->next->before = r;
        r->before = s;           // 順序逆
        s->before = r->before;   // 順序逆


ここから得られる教訓は、ポインタの付け替えは操作の順序が重要な箇所がある、ということ。誤りを犯しやすい所だし、リストの操作は扱うデータに関わらず一般性があるのだから、いちいちプログラマが直接書かなくても良くしよう・・・それが C++ の std::list コンテナでしょう。

P.S.2
参考にしたコードはリスト構造の特質を活かしているとは思えません。おそらく通常のソートプログラムで2つの要素を交換することが exchange() 関数の発想の元でしょう。でもそれがコードを不必要に難しくしている大きな要因になっています。

どなたかが指摘していたようにリスト構造では、要素をリストに挿入する、要素をリストから削除する、といった簡単な操作を用意するものです。そのほうが処理が簡単になり見通しが良くなります。具体例を示すために、恥ずかしながら「いちいち書いた」同等のコードを示してみます。

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

#define FILENAME "address.csv"
#define LINESIZE 80 /* メモリ節約w */
#define N 16        /* メモリ節約w */

typedef struct address {
    char name[N];
    char address[N];
    char tel[N];
    char mail[N];
    struct address *next;
    struct address *prev; 
} ADDR;

void ins_addr(ADDR *new, ADDR *ap)    // (*ap) の直後に (*new) を挿入
{
    new->prev = ap;
    new->next = ap->next;
    if (new->next)
        new->next->prev = new;
    ap->next = new;
}

ADDR *rmv_addr(ADDR *ap)   // (*ap)をリストから外す:remove
{
    ap->prev->next = ap->next;
    if (ap->next) {
        ap->next->prev = ap->prev;
    }
    return ap;             // そのまま返す
}

ADDR *min_addr(ADDR *ap)   // ap以降の「最小」を探す
{
    ADDR *min = ap;        // 先頭を「最小」にして検索開始

    while (ap->next) {     // 「次」があるなら
        ap = ap->next;     // その「次」と「最小」を比較
        if (strcmp(ap->name, min->name) < 0) {
            min = ap;      // 「最小」を更新
        }
    }
    return min;
}

void sort_list(ADDR *head)           // head以降をソート
{
    ADDR *min, *ap = head;

    while (ap->next) {               // 「次」がある間、繰返す
        min = min_addr(ap->next);    // 「次」以降の「最小」を見つけ
        if (min) {
            ins_addr(rmv_addr(min), ap);    // 抜出し、先頭の直後に移動する
        }
        ap = ap->next;               // 移動したばかりの所を先頭にして、繰返す
    }
}

void free_list(ADDR *ap)    // メモリ返却
{
    while (ap) {
        ADDR *n = ap->next;
        free(ap);
        ap = n;
    }
}

void show_list(ADDR *ap)    // リスト一覧表示
{
    while (ap) {
        printf("%-10s %p: next = %p, prev = %p\n",
               ap->name, ap, ap->next, ap->prev);
        ap = ap->next;
    }
    // printf("\n");
}

ADDR *line2addr(char line[])    // 読み込んだ一行を構造体に格納する
{
    ADDR *new;
    const char *token = ",\n\r";    // カンマと改行文字で区切る
    char *p;

    new = malloc(sizeof(ADDR));    // メモリ獲得
    assert(new != NULL);

    p = strtok(line, token);
    assert(p != NULL);        // "氏名の切り出しに失敗しました。"
    strcpy(new->name, p);

    p = strtok(NULL, token);
    assert(p != NULL);        // "住所の切り出しに失敗しました。"
    strcpy(new->address, p);

    p = strtok(NULL, token);
    assert(p != NULL);        // "電話番号の切り出しに失敗しました。"
    strcpy(new->tel, p);

    p = strtok(NULL, token);
    assert(p != NULL);        // "メールアドレスの切り出しに失敗しました。"
    strcpy(new->mail, p);

    return new;
}

void make_list(const char *fname, ADDR *head)    // ファイルをリストに読込む
{
    FILE *fp;
    char line[LINESIZE];

    fp = fopen(fname, "r");
    assert(fp != NULL);
    while (fgets(line, LINESIZE, fp) != NULL) {
        ADDR *n = line2addr(line);    // 一行をADDR構造体に変換し
        ins_addr(n, head);            // headの直後(リスト先頭)に挿入する
    }
    fclose(fp);
}

int main(void)
{
    ADDR head = {
        "head(dummy)", "", "", "", NULL, NULL
    };

    printf(" ... read file into list ...\n");
    make_list(FILENAME, &head);
    show_list(&head);    // 念の為 head から表示
    printf(" ... sorting ...\n");
    sort_list(&head);
    show_list(head.next);
    free_list(head.next);
    return 0;
}


リスト中の「最小」を見つけたら、交換するのではなく、削除して(リストから外して)挿入し直すだけ、隣り合うかどうかに関係なく処理できます。
line2addr() 関数を除けば、各関数は10行程度、さほど難しくないはずです。再帰も不要。
参考にしたコードはポインタ変数 struct address *head; を大元にしていますが、そこは工夫の余地があると思います。私はheadとして空の構造体を置いてみました。それもあって、ポインタのポインタはひとつも使わずに済んでいます。
他に手抜きかもしれないが、エラー判定はassert()で済ませているし、chop()関数を使わず strtok() で改行文字を削除しています。お試しあれ。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/02/13 14:53

    大分離れたところにある・・・X と XN が離れている、ということだと思いますが、
    では具体的な X と XN の番地を示していただけますか。そして XN - X を計算してみてください。どれくらい離れていますか?その数値(距離)に心当たりはありませんか?
    ついでに、Y と YN わかるといいですね。

    > *p==Y番地であるから (*p)->next は Y->next==YNでよろしいでしょうか

    惜しい。私的には正解ではない。
    「*p==Y番地であるから (*p)->next は Y->next」のことだ、とすれば、ここまでは間違いとはいわない。

    Y->next の値は Z である。よって Y->next == YN とはならない。ここが間違い。
    その値が変数のアドレスなのか、変数の値なのか、きちんと区別できること・メモリ上にデータがどのように配置されているかイメージできることがポインタを征服する条件だと私は思う。
    &(Y->next) として、&演算子で next ポインタ・・・nextフィールドと呼ぶのが正しいかも・・・のアドレスをとった、そのアドレスがYNである。
    なので「 (Y->next)のアドレス == YN 」であれば正解です。同時に q == YN なら条件が成立するというわけ。

    キャンセル

  • 2018/02/13 15:12

    はい、ありがとうございます。アドレスチェックは後で読み直すときにします。
    とりあえず、知りたかったことが理解できるところまできました。
    今日もお付き合いしていただきありがとうございます。

    キャンセル

  • 2018/02/13 15:40

    示してもらったコードを実行したら、いがいとはなれてないですね。ポインタからポインタのポインタまでは4バイトでした。文字’A'のアドレスもそんなに離れてないですね。
    図も書いて見ます。

    キャンセル

checkベストアンサー

+1

こんにちは。

(&((*p)->next) == qの条件が成立しているということは、ポインタqの値は nextのアドレスと一致しているという意味です。
ということは、qはnextを指しています。従って「next <-q」と表現してOKと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/02/04 17:43

    指摘が遅くなって申し訳ない。図中「struct address* (*p)」から、
    赤い矢印が右側構造体の next に向かい、同時に
    黒い矢印が左側構造体に向う。
    明らかな混乱です。「struct address* (*q)」も同様。

    キャンセル

  • 2018/02/05 00:40

    &演算子って、何だ?
    ポインタの値って何だ?
    ポインタのポインタって何だ?そして、これらの関係をちゃんと図示できるか(笑)?
    ポインタのポインタを使う理由は何だ?どこでポインタのポインタが作られたのか、わかっているか?等など

    今解読しようとしているコードは基本を押さえていないと惑わされます。そういう意味で、書いた人の「腕力」は窺えるし、力試しにはなるけど、センスもレベルも高くなく不必要に難しいコードになっている(ポインタのポインタを使わずに書ける問題だし、再帰など不要)、実際バグもあった、手間暇かけて勉強するような良いコードとは思えない。
    ポインタ絡みで、もう少し簡単なレベルから復習するとか、リスト構造を使うメリットを勉強し直すとか、違うアプローチを考えたほうが良いと思う。

    キャンセル

  • 2018/02/05 10:10

    ありがとうございます。

    キャンセル

0

質問の回答ではありませんが、C言語のポインタの話題で矢印を->とか<-と表記するとあらぬ誤解を受けますので(私も少々理解に時間がかかりました)、矢印は矢印記号を使った方がよろしいかと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/02/03 16:33

    そうですね図の作成と、アップに大分時間がかかりました。
    ありがとうございます。

    キャンセル

0

if (&((*p)->next) == q || &((*q)->next) == p)
ですが、あくまでもポインタ同士が等しいかどうかの判定なので、
これだけを見ると、図上の矢印の方向はどちらでもいいのだと思います。

図で矢印が必要なのはリスト構造がどのようにつながっているかを表現するためです。
構造体同士がどのように連結しているかを意識して図を描いてみたらいかがでしょう。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/02/03 14:34

    そうですよね。ありがとうございます。今作図したものを質問に貼り付ける準備と、ポインタのつなぎ替えを
    勉強しており、貼り付けは分かりましたので。図の検証をし直してからの再質問をします。

    キャンセル

-1

もしかして、

if( &((*p)->next) == q )

のこと?

それなら、if and only ifです。

if and only ifとは

向きは関係なく、pのnext の値と q が同じなら...

っていう意味。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/01/31 18:50

    すみません。回答していただいた方から図を書くとわかり易いということで、図を書いて理解しようとしています。
    回答いただいた方は、c言語のスペシャリストの方もいらっしゃるので、まよっているところです。
    2つの相反する回答を頂いたので、質問を挙げた次第です。
    あなたのおっしゃる通りです。ありがとうございます。

    キャンセル

  • 2018/01/31 21:47

    残念ながら、質問者の意図と関係ない回答だと思う

    キャンセル

  • 2018/02/03 16:40

    今日とりあえず最初の質問の図をアップしましたので。ご意見をお願いいたします。

    キャンセル

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

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

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