dポイントプレゼントキャンペーン実施中!

現場での経験もあるPGなのですが、C言語の基礎を復習していたら疑問に思うことが出てきてしまったので、質問させて下さい。

printf関数などに使われる変換指定%sについてですが、
char word[] = "test";
char *pointer;
pointer = word;
とした場合、pointerの値は文字列wordの先頭アドレスになるので、
printf ("%x", pointer);
とすれば、そのアドレス値が表示されるのはとてもよくわかるのですが、
printf ("%s", pointer);
とした場合に、"test"と表示されるのがイマイチ納得できないんです。
printf ("%s", *pointer);
なら、まだわかるんですけど・・・

変換指定の%sというものは、
「アドレスを受け取って、受け取ったアドレスにある文字列を\0がくるまで表示する」
というものなのでしょうか?

int型のポインタで同じように
printf ("%d", pointer);
とすると、pointerの値であるアドレスが10進数表示されて、pointerが指している変数の値を表示するには、
printf ("%d", *pointer);
としなければならないわけで、そういうことをいろいろ考えていたら、収拾がつかなくなってしまって(^_^;)

「とにかく%sはそういうものなの!!」と丸暗記すれば困るようなことはないのですが、どうにもモヤモヤしっぱなしなので、%sの動きについて詳しくお分かりの方がいらっしゃいましたら、ご教授下さい。

よろしくお願いしますm(__)m

A 回答 (9件)

C言語の規格上では。


%につづく変換指定子sは空白類でない文字の並びに相当し、
長さ修飾が存在しない場合、対応する実引数は文字型配列の
先頭要素へのポインタで無ければならない
配列内の終端ナル文字の直前まで書き込み精度が指定された場合、
精度を超える書き込みは行わない。精度が指定されない場合、
または精度が配列より大きい場合その配列はナル文字を含まなければ
ならないとされています。

つまり配列より小さい精度が指定されなければ受け取ったポインタを
文字配列の先頭要素とし\0が出てくるまで表示します。
    • good
    • 0

> かなり特殊な変換指定だったんですね。



私の説明が分かりづらかったかもしれません。
「%sは、なんら特殊ではない」という結論に行き着いて欲しかったのですが・・・

char* という型は、正確には文字列を指し示すものではなく、文字を指し示すものです。
ですので、*pointer と記述してしまうと、「文字列」ではなくて「文字」になってしまうんですね。
つまり "test" の先頭文字である 't' です。
't' とだけ言われても、printf は "test" と表示することはもちろん出来ません。


> puts関数の自作はかなり難しそうな気がするんですけど、とりあえず頑張ってみます(^_^;)

まずは、「文字」「文字列」「配列」「ポインタ」について復習してみてください。
基礎的な関数ですので、自作できることよりも、仕組みを理解することが重要だと思います。
    • good
    • 0
この回答へのお礼

再度のご返答ありがとうございます。

No.8さんへのご返答にも書いたのですが、%s以外の変換指定がその値そのもののビットの並びをint型などに変換するのに対して、%sはその値のアドレスが指しているビットの並びを変換しているという部分が、少し疑問に感じてしまったんです。

いろいろとご教授くださってありがとうございました。

基礎的な部分にはなるのですが、これからも勉強してみます。

お礼日時:2007/07/23 11:26

C言語で『文字列』はどう定義しますか?


・ここが理解できれば
 『char word[] = "test";』
 と
 『char *pointer = word;』
 が同じ文字列として扱えることが分かりますけど。
・『word』は char 型の配列ですが、配列名は実はアドレスを意味しています。
 よって『pointer』が char 型のポインタ(アドレス)を持っていれば char 型の配列と
 同じような扱いが出来るということです。
 だから配列とポインタは別物でも似たような使い方が出来るのです。

例えば:
char word[] = "test";
char *pointer = word;

// 配列の場合
word[0]⇒'t'
word[1]⇒'e'
word[2]⇒'s'
word[3]⇒'t'

// ポインタの場合
pointer[0]⇒'t'
pointer[1]⇒'e'
pointer[2]⇒'s'
pointer[3]⇒'t'

上記の2つは同じようにアクセスできるのです。
なぜ、ポインタなのに [ ] 演算子でも配列と同じようにアクセスできるか分かりますか?
ここがポイントですね。これを理解するには文字列の表現と配列とポインタを十分に理解
しないと駄目でしょうな。

ちなみにポインタには
*(pointer + 0)⇒'t'
*(pointer + 1)⇒'e'
*(pointer + 2)⇒'s'
*(pointer + 3)⇒'t'
としてもアクセス出来ます。
これは記述こそ違いますが [ ] 演算子と同じことですから。

余談:
printf( "test[0] = '%c'\n", "test"[0] );
printf( "test[1] = '%c'\n", "test"[1] );
printf( "test[2] = '%c'\n", "test"[2] );
printf( "test[3] = '%c'\n", "test"[3] );

この実行結果を簡単に予想できますか?
もし正しく予想できたらこの問題を質問しなくても良かったはずです。

最後に:
・『%s』は char 型配列を NULL 文字で終わる文字列だと仮定して処理します。
 よって配列を受け取る
  ↓
 アドレス(ポインタ)を受け取る
  ↓
 文字列のポインタでも受け取れる(正しく)
  ↓
 ちゃんと文字列として表示も出来る
 
 となります。
 決して『%s』が特別だったわけではありません。
 重要なのは『配列名はアドレス(ポインタ)を意味する』を理解していなかっただけです。
・以上。
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。

配列とポインタについては理解していたつもりだったんですけど(^_^;)

ただ、他の変換指定、例えば%dなどは、その値(ビットの並び)をint型に変換するものなのに対して、%sはその値をアドレスだと認識して、そのアドレスにある値を\0が出現するまで文字列として変換するという、他の変換指定とは違う動きをしている部分が疑問に感じてしまって。
それが、Cの規格としてどのように定義されているのかを知りたかったんです。
わかりにくい質問ですみませんでしたm(__)m

お礼日時:2007/07/23 11:22

>そもそも C に「文字列」なんてものは存在しない


文字列も一応規格に用語が以下のように定義されています。
最初のナル文字で終わり、かつそれを含む連続した文字の並びを「文字列」という。
    • good
    • 0

って~か, そもそも C に「文字列」なんてものは存在しない (いや, 規格には「文字列リテラル」ってあるけど...). あれは「'\0' で終わる文字の配列」だ.


で, 「配列名」は一部の場面を除いて全て「その配列の先頭要素をさすポインタ」に変換される.
だから, pointer = word; を実行すると pointer の値は「配列 word の先頭要素のアドレス」になる.
で, printf("%s\n", word); の printf の第2引数は配列名なので「その配列の先頭要素をさすポインタ」に変換される. つまり「配列 word の先頭要素のアドレス」である.
一方, printf("%s\n", pointer); における printf の第2引数は pointer であるが, その値は (上の代入により) 「配列 word の先頭要素のアドレス」である.
だから, この 2つは同じ結果でなければならない.
ちなみにポインタの値を %p 以外で変換するのは未定義動作なので, 「突然鼻歌を歌いだす」「どこかの原発をメルトダウンさせる」ということがあったとしても規格は一切関知しない.
    • good
    • 0
この回答へのお礼

配列名がその配列の先頭アドレスを指すことまではわかっていたんですけど、「"%s"がそのアドレスの指す値から\0が出てくるまでを文字列にして表示する」なんて処理をやってくださっているというのが、はっきりわかっていなくて。
今まで何気なく使っていた%sさんに感謝することにします(^^ゞ

ご返答ありがとうございました。

お礼日時:2007/07/23 11:16

#ANo.4です。


自分の書き込みで何が言いたいか分かりにくかったので補足。。。

 char str[]= "test";
 char* pointer;
 pointer = str;
 printf ("%s", *pointer);
・上記の場合の「*pointer」は既にC規格からみて
 「文字型配列の先頭要素へのポインタ」にはならない。

・精度を指定すれば、\0まで表示されない事もある。
 (精度とはprintf( "%10s", str)などの10のこと)

などなど。
    • good
    • 0
この回答へのお礼

規格として、「%sはアドレスを受け取るもの」だったんですね。
今まで何となく使っていたんですけど、こうしてはっきりわかると安心しました。

ご返答、ありがとうございました。

お礼日時:2007/07/23 11:12

そういうものです。



ためしにご自分で puts 関数を作ってみればよくお分かり頂けると思います。
内部で使っていい関数は putchar だけとし、stdout に対して出力してみてください。
(*pointer) の型は char 型なので、引数は char 型にして見ましょう。

その場合、どのように二文字目以降を表示すればよいのでしょうか。
実現できない事がお分かり頂けると思います。
    • good
    • 0
この回答へのお礼

やっぱりそうですよね。

%sは参考書などでかなり最初のほうから出てきますし、特に説明もないのであまり深く考えていなかったんですけど、かなり特殊な変換指定だったんですね。
これからは意識して使ってみることにします。

puts関数の自作はかなり難しそうな気がするんですけど、とりあえず頑張ってみます(^_^;)

ご返答ありがとうございました。

お礼日時:2007/07/20 19:57

質問の意味取り違えてたかな


*pointerはあくまでその型のサイズ1個分の内容でしかありえないですよね
    • good
    • 0

文字列は特殊と考えるほかないでしょう


char型のポインターchar *pointer;には
文字
printf ("%c", *pointer);
数字
printf ("%hhu", *pointer);
とか用意してありますので
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。

やっぱり、そう考えるしかないですよね(^_^;)

%hhuというのは初めて知りまして、実際にプログラムを書いて動かしてみたんですけど、これは文字コードを表示する変換指定なんですか?

お礼日時:2007/07/20 19:03

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!