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

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

ただいまの
回答率

87.49%

for文の動作について。

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,588

score 130

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

int main(void){
char str[] = "str == NULL ? \"(NULL)\" : str";
char *p, *q;
int ch;
p = str;
for(;;){
    for(q=p; !(*q=='?' || *q==':' || *q==0); q++) ;
    ch = *q;
    *q = 0;
    printf("|%s|\n", p);
    if(ch==0) break;
    p = q+1;
}
}


以上のプログラムについて

char str[] = "str == NULL ? \"(NULL)\" : str";
char *p, *q;
int ch;
p = str;


までの動作はわかるのですが、残りの部分の動作がいまいちわかりません。
どうか

for(;;){
    for(q=p; !(*q=='?' || *q==':' || *q==0); q++) ;
    ch = *q;
    *q = 0;
    printf("|%s|\n", p);
    if(ch==0) break;
    p = q+1;
}
}


自分なりの考え
多分、for(q=p; !(*q=='?' || *q==':' || *q==0); q++) ;に関しては、 p(ポインタ)のアドレスをq(ポインタ)に代入(箱に入れて)して、qのアドレスに入った文字列に含まれる?や:や0を探すためにqのアドレスをq++より++していき一致した場合、chに代入して、qに0を代入、これを繰り返しと思うのですが、 文字列[str == NULL][\"(NULL)\"][str"]はchに入っていると思ったのですが、なぜ printf("|%s|\n", ch); ではなく、 printf("|%s|\n", p);なのでしょうか? また if(ch==0) break;よりchに何も入らない(NULL)の後にp = q+1;とありますが、 これは何のための物でしょうか?
の部分の動作を細かく教えて頂けないでしょうか?
kazuma-sさんのような解説が一番わかりやすいです。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • maisumakun

    2021/01/31 19:58

    逆質問にはなりますが、何を意図して書いたプログラムなのでしょうか?

    キャンセル

  • carnage0216

    2021/01/31 20:28

    古い本に書いてあったプログラムです。

    キャンセル

  • 退会済みユーザー

    2021/02/01 03:28

    複数のユーザーから「やってほしいことだけを記載した丸投げの質問」という意見がありました
    「質問を編集する」ボタンから編集を行い、調査したこと・試したことを記入していただくと、回答が得られやすくなります。

  • この投稿は削除されました

回答 2

checkベストアンサー

+1

p(ポインタ)のアドレスをq(ポインタ)に代入して、

ポインタ変数 p が保持している str[0] のアドレスをポインタ変数 q に代入して、
p も q も文字列 "str = ... str" を指すようになります。

qのアドレスに入った文字列に含まれる?や:や0を探すために

q の指す文字列 "str = ... str" に含まれる '?' や ':' や '\0' を探すために

qのアドレスをq++より++していき一致した場合

q の持つアドレスを 1つずつ進めて、str[0]、str[1]、str[2]、... を *q で
見ていき、一致する文字を探します。

chに代入して、qに0を代入、

ch に代入するのは q の値ではなく、*q の値です。
それは '?' または ':' または '\0' です。

q に代入はしていません。*q に 0 (すなわち '\0')を代入しています。

文字列[str == NULL][\"(NULL)\"][str"]はchに入っていると思ったのですが、

文字列の長さは 28です。文字列の終端の '\0' を含めて 29バイトあります。
ch は int で 4バイトです。
ch に文字列を代入することはできません。
ch に代入しているのは *q の値。すなわち '?' または ':' または '\0' です。

また if(ch==0) break;よりchに何も入らない(NULL)の後に

ch==0 の 0 は NULL ではなく、'\0' です。
ch に '?' でも ':' でもなく、'\0' が入っていた場合は、
もう次に探す文字列がないので break; で for(;;) の無限ループから脱出します。

p = q+1;とありますが、これは何のための物でしょうか?

ch が '?' または ';' だった場合、次に探索する文字列をその次に進めるためです。
*q に 0 を代入して文字列を 2つに分割したあとの 2番目の文字列の先頭を
p が指すようにします。

追記
amazon で「苦しんで覚えるC言語」を検索して、「試し読み」で目次を見たら、
pp.252-354 が「第14章 文字列を扱う方法」と「第15章 ポインタ変数」だと分かりました。
説明はとても丁寧で分かりやすく書いてあります。
これを読んで、q と *q の違いが分からないはずがないと思うのですが、
まだ分からないのですね。

書名を教えてもらったので、次の質問には答えます。

q=p; はpの値をqに代入します。そして
!(*q=='?' || *q==':' || *q==0); に進む、
qに入った文字列を一つとして扱うのではなく、文字列の文字一つ一つの文字のアドレスに含まれる?:0を探すために!(*q=='?' || *q==':' || *q==0);の形にした。
しかし、なぜ*q++ではなく、q++なのでしょうか?
++してアドレスをずらしていきながら?:0をを探すのなら*q++ではないのですか?

まず、「q に入った文字列」ですが、
q はポインタ変数なので、29バイトの文字列は入りません。
q に入るのは、文字列の先頭アドレスです。

p = str; は p = &str[0]; と同じ意味です。
p には、str[0] のアドレスが入っています。
q = p を実行すると、q にも str[0] のアドレスが入ります。
p も q もポインタで、str[0] を指しているのです。
str[0] は文字列の先頭なので、p と q は文字列を指していると言っていいでしょう。

*q=='?' ですが、* は間接演算子なので、*q は q の指している変数になります。
q の値は str[0] のアドレスなので、*q は str[0] です。
*q=='?' は str[0]=='?' のことなのです。

q++ で、q が 1増えます。q の値は str[1] のアドレスになりました。
次の *q=='?' は str[1]=='?' のことなのです。

これを繰り返すと、str[12] が '?' なので、そこで forループを抜けます。
q は str[12] を指しています。q には str[12] のアドレスが入っています。

*q++ ですが、後置演算子++ のほうが、前置演算子* よりも優先順位が高いので、
*(q++) ということになります。
q++ で str[0] のアドレスを持っていた q が str[1] のアドレスを持つようになります。
ただし、後置演算子++ の演算結果は 1増えた q の値ではなく、元の値です。
ということは、 q++ の値は str[0] のアドレスのままです。
前置演算子* により *q++ は str[0] になります。
この str[0] という変数は、値を参照されることもなく、別の値を代入されること
もなく、何の意味もないことになります。

for (i = 0; a[i] != 0; i++) printf("%d\n", a[i]); を
for (i = 0; a[i] != 0; a[i++]) printf("%d\n", a[i]); と書いても同じように
動きますが、だれもそんな風には書きません。

for (q = p; !(*q == '?'); q++) を
for (q = p; !(*q == '?'); *q++) と書いても同じように動きますが、
そう書く意味はありません。

追記2
ポインタを使わずに配列だけで書いてみました。
実行手順も実行結果もポインタを使ったものと同じです。

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

int main(void)
{
    char str[] = "str == NULL ? \"(NULL)\" : str";
    int i, j;
    int ch;
    i = 0;
    for (;;) {
        for (j = i; !(str[j]=='?' || str[j]==':' || str[j]==0); j++) ;
        ch = str[j];
        str[j] = 0;
        printf("|%s|\n", &str[i]);
        if (ch == 0) break;
        i = j + 1;
    }
}


これなら理解できますか?

p は &str[i]、*p は str[i]、
q は &str[j]、*q は str[j] という関係です。

追記3

しかし、なぜ*q++ではなく、q++なのでしょうか?
++してアドレスをずらしていきながら?:0をを探すのなら*q++ではないのですか?

この質問に対して、i++ と a[i++] の例を出して、a[i++] は無意味であり、
同様に q++ で済むのに、*q++ と書くのは無意味である、
と回答しましたが、理解されなかったようですね。

#include <stdio.h>

int main(void)
{
    int a[4] = { 3, 5, 7, 0 };
    char str[4] = "abc"; // char str[5] = { 'a', 'b', 'c', '\0' }; でも同じ

    for (int i = 0; a[i] != 0; i++) printf("%d ", a[i]);
    putchar('\n');
    for (int i = 0; a[i] != 0; a[i++]) printf("%d ", a[i]);
    putchar('\n');
    for (int i = 0; a[i] != 0; &a[i++]) printf("%d ", a[i]);
    putchar('\n');

    for (int i = 0; str[i] != 0; i++) printf("%s ", &str[i]);
    putchar('\n');
    for (int i = 0; str[i] != 0; str[i++]) printf("%s ", &str[i]);
    putchar('\n');
    for (int i = 0; str[i] != 0; &str[i++]) printf("%s ", &str[i]);
    putchar('\n');

    for (char *p = str; *p != 0; p++) printf("%s ", p);
    putchar('\n');
    for (char *p = str; *p != 0; *p++) printf("%s ", p);
    putchar('\n');
    for (char *p = str; *p != 0; &*p++) printf("%s ", p);
    putchar('\n');
}


i++ や p++ に意味があるのに、それに * や [] や & を付けてもその演算結果を
使わないのであれば無意味です。
わかりませんか?

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2021/02/13 17:30

    なるほど、
    for (i = 0; str[i] != 0; i++) printf("%d\n", str[i]); を
    for (i = 0; str[i] != 0; str[i++]) printf("%d\n", str[i]);でした。

    前もってstr[i]と定義されているため
    for (i = 0; str[i] != 0; i++) printf("%d\n", str[i]);
    for (i = 0; str[i] != 0; i++) printf("%d\n", str[i]);としても良いのですね。

    キャンセル

  • 2021/02/13 17:31

    2021/02/13 17:28と2021/02/13 17:30の解釈が正しいかはいかいいえで答えて頂けるとありがたいです。

    キャンセル

  • 2021/02/13 18:15

    見落としていました。printf("%d\n", &str[i]); はダメです。
    %d は int の値を数字列に変換するためのもので、ポインタの値(アドレス) には使えません。
    printf("%d\n", str[i]); なら、str[i] の型は char ですが、値は int に変換されるので問題ありません。

    2021/02/13 17:28
    > よりa[i]はポインタの表すアドレスに入っている「数値を表す」とわかりました。
    正しいでしょうか?

    いいえ、正しくありません。
    a[i] は配列 a の i番目の要素であり、printf に渡す引数として使うなら、数値になります。
    ポインタやアドレスという用語はどこからでてきましたか?
    あるいは、a[i] が *(a + i) と同じであるという事実をご存知ですか?

    > ちなみにこのプログラムはアドレスのメモリに入った「数値」を表すためのプログラムですか?

    「アドレスのメモリに入った」が意味不明であり、「はい・いいえ」で答えられません。
    配列 str の各要素の文字の文字コードの値を 10進数で表示するものです。

    2021/02/13 17:30
    「前もってstr[i]と定義されているため」が意味不明のため答えられません。
    そのあとの 2つの for文が同じもののため、意味不明です。

    キャンセル

0

なぜ printf("|%s|\n", ch); ではなく、 printf("|%s|\n", p);なのでしょうか?

文字で無く文字列を表示するためです。
文字列としてまとめて表示せず、1文字ずつ表示する案もあるでしょうか、それを|で囲むようにする処理が面倒になりそうですね。

また if(ch==0) break;よりchに何も入らない(NULL)の後にp = q+1;とありますが、 これは何のための物でしょうか?

今表示した文字列(pからqまで)の続きを処理するためです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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