

プログラムのデバッグに関して2つ解決したいプログラムがあります。  
環境はvisual studio 2019 Windows10 64bitです。

疑問点。なぜデバッグ中のstr[]の中は "str == NULL"しか書いていないのか、  
char str[] = "str == NULL ? "(NULL)" : str";より、str[] の中には"str == NULL ?
"(NULL)" : str"が入っていると思っていましたがデバッグしてみるとそうでないとわかりました。 **なぜstr[]の文字列が "str ==
NULL ? "(NULL)"ではなくずっと"str == NULL のままなのかわかりません。**  
一回目のループで中身はstr == NULL ? "(NULL)" : str、二回目のループでstr == NULL  
三回目のループでstr == NULLとなんだか想像したのと違った結果になります。

もう一つのプログラムに関してはstr[]の文字列は  
char* p = str[]によりpのアドレスに入ったはずなのに、デバッグするとなぜかstr[]の文字列が"abcde"ではなく、  
最初はchar str[] = { "abcde" };の中身はabcdeでしたが、一回目のループでbbcde、二回目でbccde、三回目bcdde  
四回目bcdeeと変わっていきます。 **なぜ`char* p`にstr[]の文字列を入れたのにstr[]の文字列まで変化するのかわかりません。**

一つ目のコード

[code]

    #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;
        }
    }
    
[/code]

二つ目のコード

[code]

    #include <stdio.h>
    int main(void) {
        char str[] = "abcde";
    
        for (char* p = str; *p; ++p)
        {
            ++(*p);
            printf("%s\n", &(*p));
        }
    }
    
[/code]


CC BY-SA 4.0
2021-02-16T02:32:41.350
74030
2021-02-16T09:48:25.443
2021-02-16T03:26:38.373
3060
27296
question
c
char str[] をポインタで参照したときの挙動が分からない


キーワード  
メモリ。ポインター。参照。終端文字。NULL。

[code]

    #include <stdio.h>
    #include <string.h>
    
    int main(void) {
        /* メモリ上に以下の文字列と最後尾にNULLを確保します。 */
        char str[] = "str == NULL ? \"(NULL)\" : str";
    
        /* strの確保されているメモリアドレスにアクセスする */
        /* 用途として、ポインターを２つ用意します。*/
        char* p, * q;
        /* strで確保されたメモリアドレスの終端まで達したことを判断する */
        /* ための変数です。無限ループを止めるための判断材料になります */
        int ch;
        /* ポインターpをstrで確保された最初のアドレスに設定します。*/
        p = str;
    
        /* 無限ループです。breakするまで止まりません。 */
        for (;;) {
            /* もう一つのループです */
            /* pが示すstrで確保されたアドレスから、順番に参照します */
            /* 中身が ? または : または終端NULLでなければ、ループは続きます。*/
            /* つまりqの指すアドレスは、strで参照できる中身が?:NULLまで一つずつ進んでいきます。*/
            for (q = p; !(*q == '?' || *q == ':' || *q == 0); q++);
            /* chに*qの中身を一旦保存しています。0の場合のみ、後でbreakして無限ループから抜け出します。*/
            ch = *q;
            /* *qの中身を0に置き換えています。これによって、strの中身が書き換わります */
            *q = 0;
            /* *p(最初はstrの先頭)からNULLまでを表示しています。 */
            /* 後半の処理で、pのアドレスは進められています */
            printf("|%s|\n", p);
            /* 無限ループを抜け出す処理です。*/
            /* これが正常に記述できないと無限ループでプログラムが止まりません。*/
            if (ch == 0) break;
            /* strの最初を示していたpのアドレスをq+1に書き換え、参照先を進めています*/
            /* これによって、上のprintfで表示するメモリアドレスも進みます */
            p = q + 1;
        }
    }
    
    
    
[/code]

[code]

    #include <stdio.h>
    int main(void) {
        /*str[]でメモリ上に５文字分の連続したアドレスを確保し、中身をabcdeとします。*/
        char str[] = "abcde";
    
        /* strで確保された最初のメモリアドレスから一つずつ参照していきます。
        /* NULLが出るまでループは終わらない。*/
        /* このループで1文字目から終端NULLまで。2文字目から終端NULLまでと順に表示します */
        for (char* p = str; *p; p++)
        {
        /* pointer *pはstr[]で確保されたメモリ上のアドレスの中身を直接操作する仕組みです。 */
        /* str[]で確保されたメモリアドレスを直接操作できる仕組みが ポインターです。 */
            /* *pで示されているメモリアドレスの値を取り出して、そこにプラス１します */
            /* それを*pで示されているアドレスに代入しています。strで確保されたメモリの中身が書き換わります。 */
            (*p) = (*p) + 1;
            /* *pで示しているアドレスから、終わりのNULLまでを表示します。*/
            printf("*p:%s\n", &(*p));
            /* strの中身も表示します。 */
            printf("str:%s\n", str);
        }
    }
    
[/code]


CC BY-SA 4.0
2021-02-16T03:15:14.730
74031
2021-02-16T03:33:55.677
2021-02-16T03:33:55.677
43957
43957
74030
answer


[epsilon phoenix](https://ja.stackoverflow.com/users/43957/epsilon-
phoenix)さんの[回答](https://ja.stackoverflow.com/a/74031/25936)だけでは理解が進まないようでしたので補足として。

1つ目のコードに細かく`printf`を埋め込んでみました。

[code]

    #include <stdio.h>
    #include <string.h>
    
    int main(void) {
      char str[] = "str == NULL ? \"(NULL)\" : str";
      char *p, *q;
      int ch;
      p = str;
      printf("before loop\n");
      printf("  str = |%s|\n", str);
      printf("  p = |%s|\n", p);
      for (;;) {
        printf("loop start\n");
        printf("--before inner loop\n");
        printf("  str = |%s|\n", str);
        printf("  p = |%s|\n", p);
        for (q = p; !(*q == '?' || *q == ':' || *q == 0); q++)
          ;
        printf("--after inner loop\n");
        printf("  str = |%s|\n", str);
        printf("  p = |%s|\n", p);
        printf("  q = |%s|\n", q);
        ch = *q;
        *q = 0;
        printf("--after *q = 0\n");
        printf("  str = |%s|\n", str);
        printf("  p = |%s|\n", p);
        printf("  q = |%s|\n", q);
        printf("|%s|\n", p);
        if (ch == 0)
          break;
        p = q + 1;
        printf("--after p = q + 1\n");
        printf("  str = |%s|\n", str);
        printf("  p = |%s|\n", p);
        printf("  q = |%s|\n", q);
        printf("loop end\n\n");
      }
      printf("after loop\n");
      printf("  str = |%s|\n", str);
      printf("  p = |%s|\n", p);
      printf("  q = |%s|\n", q);
    }
    
[/code]

実行結果は以下のようになります。

[code]

    before loop
      str = |str == NULL ? "(NULL)" : str|
      p = |str == NULL ? "(NULL)" : str|
    loop start
    --before inner loop
      str = |str == NULL ? "(NULL)" : str|
      p = |str == NULL ? "(NULL)" : str|
    --after inner loop
      str = |str == NULL ? "(NULL)" : str|
      p = |str == NULL ? "(NULL)" : str|
      q = |? "(NULL)" : str|
    --after *q = 0
      str = |str == NULL |
      p = |str == NULL |
      q = ||
    |str == NULL |
    --after p = q + 1
      str = |str == NULL |
      p = | "(NULL)" : str|
      q = ||
    loop end
    
    loop start
    --before inner loop
      str = |str == NULL |
      p = | "(NULL)" : str|
    --after inner loop
      str = |str == NULL |
      p = | "(NULL)" : str|
      q = |: str|
    --after *q = 0
      str = |str == NULL |
      p = | "(NULL)" |
      q = ||
    | "(NULL)" |
    --after p = q + 1
      str = |str == NULL |
      p = | str|
      q = ||
    loop end
    
    loop start
    --before inner loop
      str = |str == NULL |
      p = | str|
    --after inner loop
      str = |str == NULL |
      p = | str|
      q = ||
    --after *q = 0
      str = |str == NULL |
      p = | str|
      q = ||
    | str|
    after loop
      str = |str == NULL |
      p = | str|
      q = ||
    
[/code]

**以下の回答では簡便のために必ずしも正しくない表現をしている箇所がありますが、大筋では間違っていないものと考えます。**

**前提として。**

  * **[c](/questions/tagged/c "'c' のタグが付いた質問を表示")のchar型配列の終端は`NULL`(`\0`)です。`char str[] = "str == NULL ? \"(NULL)\" : str"`は上記の文字列の直後に`\0`(`\0`で一文字です)を入れているので、正確にいうと`char str[] = "str == NULL ? \"(NULL)\" : str\0`が代入されます。**
  * **`str`はこの文字列の1文字目の`s`の位置が入っています。**
  * **`p`や`q`は文字列`str == NULL ? \"(NULL)\" : str\0`を`str`からコピーしたり受け取ったりしているわけではありません。上記の文字列の位置を入れるためのものと考えてください。**

ループである`for (;;)`に入る前、`str`も`p`も`str`に代入したままの文字列が取り出せます。  
`str`も`p`も上記の文字列の最初にある`s`の位置を指していて、この文字列の途中に`\0`がないからです。

**上記の実行結果を見てください。**  
最初のinner loopが終わったとき、`q`は`str == NULL ? \"(NULL)\" :
str\0`でいうと最初の`?`の位置を指しています。  
なので文字列を取り出そうとすると`?`から`\0`までの`? "(NULL)" : str`が得られます。  
一方、`str`も`p`もループに入る前から変更されていないので、現状では最初の文字列がそのまま取り出せます。

そして`*q = 0`によって文字`?`が文字`\0`になります。  
**`*q`は`q`の位置にある文字として扱われるからです。`0`が代入されたのに`\0`なの？という疑問は今はそういうものだと思って受け入れてください。**  
`*q = 0`によって文字列は`str == NULL \0 "(NULL)" :
str\0`に変更されたので、`str`も`p`も`s`から`\0`までの`str == NULL `までが文字列として取り出されます。  
`q`は指している位置の文字が`?`から`\0`に変わったので取り出す文字列は空になります。

そして`p = q + 1`で`p`の指す位置(文字列を取り出す開始位置)が`q`の1つ先になります。最初の`"`の直前の半角空白の位置です。  
文字列を取り出すと` "(NULL)" : str`となります。

以降、 **上記の実行結果を見ていけば`str`と`p`と`q`がどのように変化していくか見て取れると思います。**

もし、この回答が参考になるのでしたら2つめのプログラムは是非ご自分の手で細かく`printf`を埋め込んで動作を追ってみてください。


CC BY-SA 4.0
2021-02-16T06:41:43.733
74042
2021-02-16T09:48:25.443
2021-02-16T09:48:25.443
25936
25936
74030
answer