#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さんのような解説が一番わかりやすいです。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/01/31 11:28
回答2件
0
ベストアンサー
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
ポインタを使わずに配列だけで書いてみました。
実行手順も実行結果もポインタを使ったものと同じです。
C
1#include <stdio.h> 2#include <string.h> 3 4int main(void) 5{ 6 char str[] = "str == NULL ? \"(NULL)\" : str"; 7 int i, j; 8 int ch; 9 i = 0; 10 for (;;) { 11 for (j = i; !(str[j]=='?' || str[j]==':' || str[j]==0); j++) ; 12 ch = str[j]; 13 str[j] = 0; 14 printf("|%s|\n", &str[i]); 15 if (ch == 0) break; 16 i = j + 1; 17 } 18}
これなら理解できますか?
p は &str[i]、*p は str[i]、
q は &str[j]、*q は str[j] という関係です。
追記3
しかし、なぜ*q++ではなく、q++なのでしょうか?
++してアドレスをずらしていきながら?:0をを探すのなら*q++ではないのですか?
この質問に対して、i++ と a[i++] の例を出して、a[i++] は無意味であり、
同様に q++ で済むのに、*q++ と書くのは無意味である、
と回答しましたが、理解されなかったようですね。
C
1#include <stdio.h> 2 3int main(void) 4{ 5 int a[4] = { 3, 5, 7, 0 }; 6 char str[4] = "abc"; // char str[5] = { 'a', 'b', 'c', '\0' }; でも同じ 7 8 for (int i = 0; a[i] != 0; i++) printf("%d ", a[i]); 9 putchar('\n'); 10 for (int i = 0; a[i] != 0; a[i++]) printf("%d ", a[i]); 11 putchar('\n'); 12 for (int i = 0; a[i] != 0; &a[i++]) printf("%d ", a[i]); 13 putchar('\n'); 14 15 for (int i = 0; str[i] != 0; i++) printf("%s ", &str[i]); 16 putchar('\n'); 17 for (int i = 0; str[i] != 0; str[i++]) printf("%s ", &str[i]); 18 putchar('\n'); 19 for (int i = 0; str[i] != 0; &str[i++]) printf("%s ", &str[i]); 20 putchar('\n'); 21 22 for (char *p = str; *p != 0; p++) printf("%s ", p); 23 putchar('\n'); 24 for (char *p = str; *p != 0; *p++) printf("%s ", p); 25 putchar('\n'); 26 for (char *p = str; *p != 0; &*p++) printf("%s ", p); 27 putchar('\n'); 28}
i++ や p++ に意味があるのに、それに * や [] や & を付けてもその演算結果を
使わないのであれば無意味です。
わかりませんか?
投稿2021/01/31 13:17
編集2021/02/13 07:57総合スコア8224
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/01/31 13:25
2021/01/31 13:28
2021/01/31 14:25
2021/01/31 14:54 編集
2021/02/01 03:33
2021/02/01 03:40
2021/02/01 04:04
2021/02/01 04:08
2021/02/01 04:12
2021/02/01 04:14
2021/02/01 04:39
2021/02/01 21:41 編集
2021/02/01 21:01 編集
2021/02/01 22:44
2021/02/02 06:32
2021/02/02 06:36
2021/02/02 15:14
2021/02/02 17:02 編集
2021/02/02 16:41
2021/02/03 03:08
2021/02/05 22:31
2021/02/13 08:25 編集
2021/02/13 07:32
2021/02/13 08:23
2021/02/13 08:26
2021/02/13 08:28
2021/02/13 08:30
2021/02/13 08:31
2021/02/13 09:15
0
なぜ printf("|%s|\n", ch); ではなく、 printf("|%s|\n", p);なのでしょうか?
文字で無く文字列を表示するためです。
文字列としてまとめて表示せず、1文字ずつ表示する案もあるでしょうか、それを|
で囲むようにする処理が面倒になりそうですね。
また if(ch==0) break;よりchに何も入らない(NULL)の後にp = q+1;とありますが、 これは何のための物でしょうか?
今表示した文字列(p
からq
まで)の続きを処理するためです。
投稿2021/01/31 11:56
総合スコア85888
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。