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

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

ただいまの
回答率

87.79%

文字列とポインタを扱ったプログラムの疑問。

受付中

回答 4

投稿 編集

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

score 128

#include <stdio.h>
int main(void) {

    char str[] = { "abcde" };

    for (char* p = str; *p; ++p)
    {
        ++(*p);
        printf("%s\n", &(*p));
    }

}
結果は
bbcde
ccde
dde
ee
f


ただなんでこのような結果になるのか自分で書いて置きながらわからないので、自分なりに解説すると同時に疑問点を上げていきます。

まずchar* p = strより、strに入っている文字列abcdeをchar* p により、ポインタpの指すアドレスの番号ではなく、アドレスに入っている文字列abcde(文字コード)が渡される。そして、for文の++pにより文字列のアドレスに入っている文字コードの数値が+1されるのでポインタpが先頭のアドレスから表す文字列はbcdeとなる、それから*pには文字コードが一つしか入らないようなので、「b」だけが入る、そして、なぜかデバッグの結果より[0]に*pに代入されたbが代入され、 strの中身の数列はbcdeとなっているので、なんで[6]にbが代入されないのかわからないですが、*pに代入されたbが代入されたbが[0]として、bbcdeとなりました。

太い黒字で書いた部分の疑問がわかりません。

  1. 疑問1 なぜ*pには文字コードが一つしか入らないのか。
  2. 疑問2 なぜ[0]に*pに代入されたbが代入されたのか、個人的には文字列は最後に表示するためてっきりbcdebだと思っていました。
    デバッグの画像です。
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • m.ts10806

    2021/02/16 07:24

    >https://teratail.com/questions/322641#reply-447826
    >言語はcです。今後はタグにcのみにします。

    大ウソつき。自分が言ったことすら守れないってどういう神経ですかね。

    キャンセル

  • m.ts10806

    2021/02/16 10:04

    >太い黒字で書いた部分の疑問がわかりません。

    自分の文章自分で理解できてますか?読むの赤の他人ってこと認識できてますか?

    キャンセル

  • m.ts10806

    2021/02/16 10:05

    あ、いや。認識できてたらここまでこじれないか。

    キャンセル

回答 4

+2

やってる事の流れとしては

char str[] = { "abcde" };
はchar型のstr配列を宣言して"abcde"で初期化しています。

char* p = str
はポインタpを宣言してstr配列の一番最初の文字のポインタを代入しています。
&str[0]strは同じです。)

++p
はpのポインタに加算しています。(ループの最後に毎回実行される)

++(*p)
pのポインタの指す場所の数値(文字コード)に1加算しています。
「a」の文字コードに1加算すると「b」になります。

printf("%s\n", &(*p));
pポインタから文字列を表示。
(ちなみにprintf("%s\n", p);←こう書いても同じです。)

以後、pのポインタの指す場所の数値が0(ヌル文字)になるまでfor内を繰り返す。

>>疑問1 なぜ*pには文字コードが一つしか入らないのか。

pはポインタだからポインタしかしか入りません。

pとだけ書くと今代入されてるポインタになります。
*pと書くと今代入されているポインタの指す場所の数値になります。

printf関数で書式に%sを指定した場合、引数に指定するのは文字列の配列のポインタです。
pを指定して文字が表示されるのは、文字列の配列のポインタが代入されているからです。

>>疑問2 なぜ[0]に*pに代入されたbが代入されたのか、

++pでポインタを加算しながら++(*p)でポインタが指してる場所の数値(文字コード)を加算してるので、str配列の全ての文字がズレて次の文字になってるだけです。

>>個人的には文字列は最後に表示するためてっきりbcdebだと思っていました。

恐らくポインタの仕組みが理解できていないか誤解をしているようです。
正直あなたがどういった誤解をしているのかわからないので、質問の意図も良く分かりません。

↓この辺のページでポインタを勉強をする事をお勧めします。
https://programming.pc-note.net/c/pointer.html
http://www.itsenka.com/contents/development/c/pointer.html
https://monozukuri-c.com/langc-pointer-interpretation/
https://www.cc.kyoto-su.ac.jp/~yamada/programming/pointer.html

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2021/02/16 07:06

    ありがとうございます。
    >>なぜ*pには文字コードが一つしか入らないのか。
    について、書いていただいた理由によりどんなプログラムにしても*pには
    >>*pと書くと今代入されているポインタの指す場所の数値になります。
    よりそのポインタの表す場所のみの数値あるいは文字しか入らないため値あるいは文字が一つしか入らないということでしょうか?

    キャンセル

  • 2021/02/16 07:22

    ポインタ型で宣言した変数にポインタ以外を入るって考え方自体が間違ってます。
    ポインタ型で宣言した変数はポインタしか入れてはいけません。

    *pと表記すると代入されているポインタが指している場所に「アクセス」できるって考えた方がいいです。
    *pでアクセスできるのは一か所だけって事です。

    だから配列の次の数値にアクセスするには、p++と表記して指してるポインタを加算する必要があります。

    とりあえず、リンク先に詳しく書いてあるんで読んでみて下さい。

    キャンセル

+1

まずchar* p = strより、strに入っている文字列abcdeをchar* p により、ポインタpの指すアドレスの番号ではなく、アドレスに入っている文字列abcde(文字コード)が渡される。

この段階で違っています。

char* p


で用意されるのは、文字列の入った領域のアドレスを収納するポインタ一つです。

char* p = str


では、文字配列 str の先頭アドレスが、文字列ポインタ p にコピーされます。
文字コードのコピーは行われません。

疑問2 なぜ[0]に*pに代入されたbが代入されたのか、個人的には文字列は最後に表示するためてっきりbcdebだと思っていました。

そして、for文の++pにより文字列のアドレスに入っている文字コードの数値が+1されるのでポインタpが先頭のアドレスから表す文字列はbcdeとなる、

for 文の今回 ++p となっている部分の実行は毎回のループの最後で行われるので、
第一回目のループでは、 

++(*p);

を実行した場合、一回目の段階ではポインタp の内容は最初のまま、
つまり、文字配列 str の領域の先頭アドレスなので、
ポインタp の指す領域の値は、"abcde" (str の値)の先頭一文字('a')がカウントアップされて 'b' となり、"bbcde" となります。

疑問1 なぜ*pには文字コードが一つしか入らないのか。

それから*pには文字コードが一つしか入らないようなので、「b」だけが入る、

char *p

と宣言した場合、
p は 文字列領域のアドレスを持つポインタ
*p はそのポインタの指すchar型の変数という意味になります。
char 型は文字一つを示す型なので、文字コード一つしか入らないのは当たり前です。
この部分は完全に理解していないとポインタを使ったプログラムはできません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2021/02/16 12:13

    >>まずchar* p = strより、strに入っている文字列abcdeをchar* p により、ポインタpの指すアドレスの番号ではなく、アドレスに入っている文字列abcde(文字コード)が渡される。
    なるほど、先頭のアドレスのみが入るのですね。
    そして、*pによりアドレスのメモリの値を取るので、ポインタpのアドレスに入った先頭のアドレスの文字コードが入るのですね。

    ちなみにcharは文字コードを受け取るための方ですか?

    またhttps://teratail.com/questions/322763に協力して頂けないでしょうか?

    キャンセル

+1

ポインタそのものをインクリメントしたりするとややこしさがますので、添字を使って書き換えてみましょう。

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

さて、ここでstrは配列型ですが、配列型はいくつかの例外を除いて常に配列の先頭要素へのポインタとして読み替えられるのでした(array to pointer conversion)。つまり&str[i]str + iと等価です。なので

#include <stdio.h>
int main(void) {
    char str[] = { "abcde" };
    for (size_t i = 0; str[i]; ++i)
    {
        ++str[i];
        printf("%s\n", str + i);
    }
}

のように書くこともできますね。

さて、このように書き換えると

疑問1 なぜ*pには文字コードが一つしか入らないのか。

はそもそも変数pを排除したのでなくなり

疑問2 なぜ[0]に*pに代入されたbが代入されたのか、

++str[i]のようになったので、配列の要素をインクリメントしていることがより明確にわかります。

このように添え字を使ってアクセスするとわかりやすくなると思います。

・・・と追うような話が前橋和弥氏の「C言語ポインタ完全制覇」にかかれていますので読んでみてください。ふるい方でも新版でも大丈夫です。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2021/02/16 11:25

    凄くわかりやすいです。
    コードを簡略化すると本質が見えてきます。

    後出来れば
    https://teratail.com/questions/322763
    の質問にも答えて頂けないでしょうか。

    キャンセル

  • 2021/02/16 20:11

    他の回答者の方から十分に回答が付いてるので特に追加で回答する予定はありません。わからないことを質問する、それ自体もエネルギーが必要なことでその努力は素晴らしいと思います。また回答者たちはまあなんだかんだ言いながら具体的な質問に対しては回答をするでしょう。しかしもしC言語を理解したいというところに目標を置くならばあなたに今必要なのは質問をすることではなく、なにかまともな本かWebサイトをじっくり読むことだろうと思われます。たくさんの質問をする中で体系的に知識が体得できることは相当なレアケースであり、あくまでまずは良質な読み物をベースに行うべきだと思います。私や他の回答者のかたがすでに適当な読み物については紹介をしていますから、急がば回れ、まずは一度立ち止まってそれらを読みすすめる、そして理解したことを文章にまとめるという作業をする(ブログやQiitaなどで人に読まれるかもしれない環境を意識するとなおよし)、その上でわからないことが当然出てきますから質問をする、こうすることが大事なのではないでしょうか?

    キャンセル

0

等価なコードをポインタ使わずに書いてみました:

#include <stdio.h>
int main(void) {

    char str[] = { "abcde" };

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

}


さて、これでも

1 なぜ str[0] には文字コードが一つしか入らないのか。
2 なぜ str[0] にbが代入されたのか、個人的には文字列は最後に表示するためてっきりbcdebだと思っていました。

って疑問を抱きますか?

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2021/02/16 11:35

    ところで僕のこの回答、yumetodoさんのとまったく同じです。
    yumetodoさんのにあなたの反応がないのが不可解。

    キャンセル

  • 2021/02/16 12:02

    評価と反応しましたよ。

    キャンセル

  • 2021/02/16 12:04

    僕の誤認です、失礼。

    再度: 僕の回答は不要ではありませんでしたか? 撤回しますか?

    キャンセル

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

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

関連した質問

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