![](http://oshiete.xgoo.jp/images/v2/pc/qa/question_title.png?e8efa67)
#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 なぜ*pには文字コードが一つしか入らないのか。
疑問2 なぜ[0]に*pに代入されたbが代入されたのか、個人的には文字列は最後に表示するためてっきりbcdebだと思っていました。
No.6ベストアンサー
- 回答日時:
#3です。
補足に対して。
>なぜ&(*p)は*をつけないとならないのですか?
C言語で変数を宣言すると、その変数の大きさに合わせたメモリ領域が確保されます。
これはポインタ変数といえどもまったく同様です。
char* p;
と宣言するとポインタ変数分のメモリ領域が確保され、それがpという名前に紐づけられます。
メモリ上に領域があるということは、その領域を示すアドレスが存在する、ということです。
そのアドレスが&pの意味するものです。確かに&pもポインタ型ではあるのですが、さしているのはpそのものであり、pが指し示しているものとは別物なのです。
例えば、
char* p;
char str[]="abcde";
と宣言するとp,str[0]~str[5]の領域が確保されます。
それぞれのアドレスを
p:50
str[0]:100
str[1]:101
str[2]:102
str[3]:103
str[4]:104
str[5]:105
としましょう。str[0]~str[5]は必ず連続して領域が確保されます。
p=str;
とすると、文字型へのポインタpにstr[0]のアドレスが代入されます。
この時点でメモリ上の50番地に"100"というデータが書き込まれます。
ではこの段階で&pと&(*p)がどのような値となるか考えてみましょう。
&pはpというポインタ変数が収められているアドレスです。つまり、&pの値は50です。
&(*p)とはポインタpに格納されているアドレスにおいてあるデータが*pですので、*pのアドレスとはそのデータのおいてあるアドレスを意味します。
この段階でpの中には100というデータが入っているので、*pはアドレス100に入っている'a'を意味します。
&(*p)は'a'というデータがおいてあるアドレス、つまりは100です。これはpに格納されているデータと同一です。
すごいわかりやすいです!
是非先生になってほしいくらいです。
良ければこちらも答えて頂けるとありがたいです。https://teratail.com/questions/322870
No.5
- 回答日時:
No.4の補足
Single byte と Double byte の文字の違いによるものかどうかご確認ください。
"?" は "?"と同じではありませんし、":"は":"と同じではありません。
![「#include <stdio.h> i」の回答画像5](http://oshiete.xgoo.jp/_/bucket/oshietegoo/images/media/5/543074610_602b277fb4c62/M.png)
No.4
- 回答日時:
GCC環境でも期待通りの出力が可能です。
locale設定を確認してください。$ cat 322763.c
#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;
}
}
$ gcc -o 322763 322763.c
$ ./322763
|str == NULL |
| "(NULL)" |
| str|
$
$ locale
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
$
![「#include <stdio.h> i」の回答画像4](http://oshiete.xgoo.jp/_/bucket/oshietegoo/images/media/5/543074610_602b277fb4c62/M.png)
No.3
- 回答日時:
char* p;
と宣言するとpが示すのは文字1個を示すデータのアドレスにすぎません。
pが文字列を表すわけではありません。
*pが表すのは文字1個だけです。
そもそもC言語には文字列というデータ型はありません。連続したメモリ領域に文字が連続して格納され\0で終端されているにすぎません。
char str[]="abcde";
と宣言するとメモリ上に少なくとも6バイトの連続した領域が取られます。
先頭の'a'のアドレスを100とすると
100:'a'
101:'b'
102:'c'
103:'d'
104:'e'
105:'\0'
のアドレスにデータが配置されます。必ず連続に配置される、という点と終端が\0になっている、という点が重要です。
p=str;
と代入するとpにstr[]が表す配列の先頭のデータのアドレスが代入されます。
つまり、'a'のアドレス100がpに代入されます。
質問者はこの時点でなぜかpに文字列"abcde"が渡されていると思い込んでいますが、pに代入されているのはあくまで'a'のアドレスにすぎません。
pには、指示しているものが文字列の先頭である、という情報すら渡されていません。
for(;;)の中の*pは*pが0,文字としては'\0'であればループから抜けるということですがここでは*pは'a'となっており0ではないため{}の中を実行します。
++(*p);
ここではpが示すアドレスにあるデータ'a'を1増やします。'a'の文字コードを1増やすと'b'の文字コードになります。これでaの文字がbに変化します。
あとは変化しません。
100:'b'
101:'b'
102:'c'
103:'d'
104:'e'
105:'\0'
pの値 100
printf("%s\n", &(*p));
何か馬鹿なことをしています。何ですか、&(*p) 単にpでいいのに何やってるの?
pがアドレスを示し、*pがそのアドレスに入っているデータを表す。&をつけてもアドレスを示すだけでpの値が得られるだけです。
で
printf("%s\n", p);
がやっていることが何かを確認しておきましょう。
printf()関数は初めの引数にあるエスケープ文字%sから、第二引数pのさすアドレスから連続して文字を表示します。そして終端文字\0が出てくるまでそれを続けます。
つまり、pの値100のアドレスにある文字'b',101のアドレスにある文字'b',102のアドレスにある文字'c',...,104のアドレスにある文字'e'をバッファに収め、次のアドレス105に終端文字\0を見つけるとバッファにある文字を画面に出力します。
さらに\n,つまりは改行を表示します。
この時の出力は
bbcde改行
100:'b'
101:'b'
102:'c'
103:'d'
104:'e'
105:'\0'
pの値 100
となります。
ここでfor()次のブロックの1回目が終了、for(;;)の最後の++pを実行します。
++p;
単にpを1個分増やします。今回の場合、文字1個につきメモリ領域1個分ですので100が1増えて101になります。
100:'b'
101:'b'
102:'c'
103:'d'
104:'e'
105:'\0'
pの値 101
++(*p);
この段階でpの値は101です。101のアドレスにある'b'の文字コードを1増やすと'c'の文字コードになります。これで101のアドレスにあるbの文字がcに変化します。
あとは変化しません。
100:'b'
101:'c'
102:'c'
103:'d'
104:'e'
105:'\0'
pの値 101
printf("%s\n", p);
pの値101のアドレスにある文字'c',102のアドレスにある文字'c',...,104のアドレスにある文字'e'をバッファに収め、次のアドレス105に終端文字\0を見つけるとバッファにある文字を画面に出力します。
さらに\n,つまりは改行を表示します。
この時の出力は
ccde改行
100:'b'
101:'c'
102:'c'
103:'d'
104:'e'
105:'\0'
pの値 101
という風になります。
なお、この間にstrの値は常に100で不変です。
また、str[]で表される文字列の大きさも変わりません。
この段階でstr[]に入力されているデータは"bccde"と同一です。
printf("%s\n"",p)がcから表示しているのはpの値が101だからです。
これ以降については自分で考えましょう。
質問者の疑問について
疑問1 なぜ*pには文字コードが一つしか入らないのか。
pが文字1個分の変数へのポインタとして宣言されているからです。
繰り返し言いますが、C言語には文字列型、という型はありません。
pに入っているデータは単なるアドレスだけで、指し示す先のデータのサイズや型はp自体はデータとしては持っていません。(*pの型はコンパイラが認識している)
疑問2
意味不明 [0]とか何?
なるほどありがとうございます。
ちなみに、
なぜ&(*p)は*をつけないとならないのですか?
詳しく知りたいです。
どうせポインタと同じなら&(p)でも良いのではないでしょうか?
また、&*(str+i)に関しても、ポインタならば
*はいらないのではないでしょうか?
No.2
- 回答日時:
ご質問にお答えすると、
- Cの演算子は文字を個別に操作しますが、文字列全体に対しては操作しません。そのために、文字列を操作するには関数を使う必要があります。
- printf("%s\n", &(*p)) の %s はポインタの指し示すアドレスのメモリー場所の内容だけでなく、印刷のために隣接する内容も取る関数です。
str: (Type = char *- 割り当てられたメモリー場所のアドレスを格納するメモリー場所である)
[a][b][c][d][e] <- 内容
| | |
| | |__アドレス (1004)
| |__アドレス (1001)
|__アドレス (1000) = strの値
* 1000は例である
p: (Type = char * - アドレスを格納するメモリー場所である)
[1000] = pの値
|
|__アドレス (8120) = &pの値
* 8120は例である
変数宣言中に :
char *p <--- pは、アドレスを格納するメモリー場所である
コード中に:
&(*p) (Type = char * - アドレス)
^ ^^
| | |___アドレス
| |___内容
|___アドレス
&pの値は アドレス (8120) である
pの値は アドレス(1000) である
*p <--- *p は、pで指し示すアドレス (1000) のメモリー場所の内容 ('a') である
*P の値は 'a' である。もちろん、文字(char) ポインタの内容が文字である
&(*p) <--- *pの内容のメモリー場所のアドレスである.
&(*p) の値は アドレス(1000)である
printf("%s\n", &(*p))
%s - char *を受け取り、それの指し示すアドレス (1000) のメモリー場所の内容をNULL終端の文字列 ("abcde\0") として印刷します
![「#include <stdio.h> i」の回答画像2](http://oshiete.xgoo.jp/_/bucket/oshietegoo/images/media/5/543074610_602b277fb4c62/M.png)
すんごいわかりやすいです。
どうか
teratailというサイトのhttps://teratail.com/questions/322763にもお答えして頂けないでしょうか?
No.1
- 回答日時:
Dry runをご覧ください。
Loop 1 :
p -> str[0]
*p = a
str = "abcde"
++(*p) = a -> b
str => bbcde
&(*p) = bbcde
Loop 2 :
p -> str[1]
*p = b
str = "bbcde"
++(*p) = b -> c
str => bccde
&(*p) = ccde
Loop 3 :
p -> str[2]
*p = c
str = "bccde"
++(*p) = c -> d
str => bcdde
&(*p) = dde
Loop 4 :
p -> str[3]
*p = d
str = "bcdde"
++(*p) = d -> e
str => bcdee
&(*p) = ee
Loop 5 :
p -> str[4]
*p = e
str = "bcdee"
++(*p) = e -> f
str => bcdef
&(*p) = f
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# str[j++]の意味 2 2022/08/30 16:20
- C言語・C++・C# c言語でユーザ関数を利用して入力された文字列を反転させるプログラムを作りたいです。 3 2023/01/29 19:47
- その他(プログラミング・Web制作) python質問 1 2023/08/14 11:54
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- Java Javaの問題なのですが、「3文字以上の英数字文字列を入力し、文字列の中に文字(9)が出てくるまでの 1 2023/06/06 18:55
- C言語・C++・C# c言語 コマンドライン引数 4 2023/02/09 18:47
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語にて構造体のメンバがNULL...
-
char*を初期化したいのですが
-
CStringからchar*への型変換に...
-
new charとnew char[N]の違いは?
-
SubStringの使い方について
-
カンマで区切った文字の抽出に...
-
文字列の途中から途中までを抽出
-
コマンドライン引数 *argv[]は...
-
'\\0'とはなんですか?
-
C言語の文字リテラル中の16進文...
-
strcpy関数で文字型変数へのポ...
-
関数について
-
char[]をDWORDに格納するには
-
動的メモリの初期化方法について。
-
DWORDとcharの変換
-
csvファイルをfscanfで読み込む...
-
fstream型オブジェクトを関数の...
-
エクセルのMID関数は、C言語では?
-
C言語のintとcharの違いってな...
-
構造体・ビットフィールドのvol...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
char*を初期化したいのですが
-
CStringからchar*への型変換に...
-
C言語のintとcharの違いってな...
-
C言語にて構造体のメンバがNULL...
-
小数点入りの文字列をfloat型に...
-
strcat関数を自作したいです
-
C言語のプログラムについてです
-
const char* s1とただのchar s1...
-
DWORDとcharの変換
-
char型にint型の数値を代入する。
-
文字列の途中から途中までを抽出
-
new charとnew char[N]の違いは?
-
文字列内の数字削除
-
csvファイルをfscanfで読み込む...
-
fgetc( )の戻り値はなぜ整数??
-
char 文字列型 の表現範囲が-12...
-
fstream型オブジェクトを関数の...
-
エクセルのMID関数は、C言語では?
-
ポインタを使って回文かどうか...
-
ポインタ配列
おすすめ情報
なるほどありがとうございます。
ちなみに、
なぜ&(*p)は*をつけないとならないのですか?
詳しく知りたいです。
どうせポインタと同じなら&(p)でも良いのではないでしょうか?
また、&*(str+i)に関しても、ポインタならば
*はいらないのではないでしょうか?