C言語関係掲示板

過去ログ

No.1101 printf("%s\n", *mnthp[0]); がまずい理由

[戻る] [ホームページ]
No.1463

ポインタでの表示
投稿者---JUN(2004/03/11 01:43:20)


以前よりここの掲示板でお世話になっています。
今回もお尋ねしたいことがあるのでよろしくお願いします。

現在「10−3.ポインタと文字列」をやっていて思ったのですが、

char *mnthp[3] = { "January","February","March"};
とした場合には
printf("%c\n", *mnthp[0]);
とすれば「mnthp[0]番地(と言うのでしょうか?)の中に格納されたアドレスが指す値」
つまり"J"が表示されると思います。
しかし、%cではなく%sで表示する場合に
printf("%s\n", *mnthp[0]);
とするのではなく
printf("%s\n", mnthp[0]);
とするのはどうしてなのでしょうか?

私は"*"をつけると値を、"*"をつけないとアドレスを操作できるというように解釈していたため
printf("%s\n", mnthp[0]);
とするとmnthp[0]の中に格納されているアドレスを文字列として表示しようとするような気がしてしまいます。

("%s\n", mnthp[0]);とするのはどうしてなのでしょうか?
説明してくださる方がおられましたら、よろしくお願いします。


No.1464

Re:ポインタでの表示
投稿者---C超初心者(2004/03/11 11:29:44)


>char *mnthp[3] = { "January","February","March"};
>とした場合には
>printf("%c\n", *mnthp[0]);
>とすれば「mnthp[0]番地(と言うのでしょうか?)の中に格納されたアドレスが指す値」
>つまり"J"が表示されると思います。
>しかし、%cではなく%sで表示する場合に
>printf("%s\n", *mnthp[0]);
>とするのではなく
>printf("%s\n", mnthp[0]);
>とするのはどうしてなのでしょうか?


私の解釈の仕方ですが、%sの場合、mnthp[0]として"January"の
先頭アドレス、つまり'j'のアドレスを渡します。そこから'\0'が
見つかるまで表示させる。という処理しているのではないでしょうか?

*mnthp[0]としてしまうと、'j'という値だけ渡されて他の"anuary"は
どう表示させればいいでしょうか?(コンピュータの立場で)

文字列の場合は複数のアドレスにわたって格納されているため、
このような処理にしていると私は思ってます(^_^;)


No.1485

Re:ポインタでの表示
投稿者---通りすがり(2004/03/18 01:44:15)


>*mnthp[0]としてしまうと、'j'という値だけ渡されて他の"anuary"は
>どう表示させればいいでしょうか?(コンピュータの立場で)

'J'が渡されると言うよりmnthp[0]のポインタがわたされるのではないでしょうか。

*mnthp[0]には"January"のアドレスが入っていてprintf関数は%sのときポインタを受け取ります。

printf("%s\n",mnthp[0]);

だとprintf()はmnthp[0]の中身を参照して"January"のポインタを取得しますけど

printf("%s\n",*mnthp[0]);

とすると、printf()にはmnthp[0]自体のアドレスをおくるのではないでしょうか?

まあ簡単に言うとmnthp[0]という箱の中の場所をおくるか箱の場所をおくるかの違いと、私は解釈してます。


No.1491

Re:ポインタでの表示
投稿者---NykR(2004/03/18 16:12:22)


>'J'が渡されると言うよりmnthp[0]のポインタがわたされるのではないでしょうか。

違います。単項*演算子は(オペランドがオブジェクトを指すポインタである場合)オペランドが指すオブジェクトの左辺値を返します。
mnthp[0] の値は、配列 "January" の先頭要素へのポインタなので、
*mnthp[0] は "January"の先頭要素の左辺値になります。
"January"の先頭には値 'J' が入っているので、printfには 'J'が渡されることになります。


No.1465

Re:ポインタでの表示
投稿者---YuO(2004/03/11 12:26:34)


>printf("%s\n", *mnthp[0]);
>とするのではなく
>printf("%s\n", mnthp[0]);
>とするのはどうしてなのでしょうか?

%sはchar *型に対して使う物だからです。


>私は"*"をつけると値を、"*"をつけないとアドレスを操作できるというように解釈していたため

そんな解釈は捨て去ってください。
というか,ポインタがアドレス,などという考えも捨て去った方がよいです。
#そんなの実装情報に過ぎない。

ポインタが関連するプログラムを組む上で重要なのは,
  • そのポインタがどのオブジェクトを指しているか
  • そのポインタの型は何か
です。


まず,
char *mnthp[3];
と宣言されたmnthpというオブジェクトの型は,char *[3]型です。
#型自体は識別子(変数等の名前)を抜けばできあがる。

これをどう読むかというと,基本的には後ろから読みます。
ただし,識別子を囲む括弧がある場合,最初に括弧内を読みます。
char *[3]型だと,
  • 3個の要素からなる配列型
  • 配列の要素はポインタ型
  • ポインタの参照型はchar型
となります。
英語だと一気に表記できて,
array [3] of pointer to char
という型になります。

次に,*mnthp[0]という部分式は,演算子の優先順位に従って,
[0]が最初に処理され,次に*が処理されることになります。

まず[0]は最初の「配列型」という部分を処理します。
つまり,「3個の要素からなる配列型」の先頭要素を要求していることになります。

mnthp[0]は,次のような型になります。
  • ポインタ型
  • ポインタの参照型はchar型
英語で書くと,
pointer to char
です。

そして,*は「ポインタ型」の部分を処理するので,*mnthp[0]はchar型になります。


>printf("%s\n", mnthp[0]);

上記の順序で書き直すと,
char * mnthp0 = mnthp[0]; /* char * mnthp0 = "January"; */
printf("%s\n", mnthp0);

と同じことです。

この時にJanuaryと表示させるために,
printf("%s\n", *mnthp0);

としないことはわかっていますよね?