現場での経験もある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
No.4ベストアンサー
- 回答日時:
C言語の規格上では。
%につづく変換指定子sは空白類でない文字の並びに相当し、
長さ修飾が存在しない場合、対応する実引数は文字型配列の
先頭要素へのポインタで無ければならない
配列内の終端ナル文字の直前まで書き込み精度が指定された場合、
精度を超える書き込みは行わない。精度が指定されない場合、
または精度が配列より大きい場合その配列はナル文字を含まなければ
ならないとされています。
つまり配列より小さい精度が指定されなければ受け取ったポインタを
文字配列の先頭要素とし\0が出てくるまで表示します。
No.9
- 回答日時:
> かなり特殊な変換指定だったんですね。
私の説明が分かりづらかったかもしれません。
「%sは、なんら特殊ではない」という結論に行き着いて欲しかったのですが・・・
char* という型は、正確には文字列を指し示すものではなく、文字を指し示すものです。
ですので、*pointer と記述してしまうと、「文字列」ではなくて「文字」になってしまうんですね。
つまり "test" の先頭文字である 't' です。
't' とだけ言われても、printf は "test" と表示することはもちろん出来ません。
> puts関数の自作はかなり難しそうな気がするんですけど、とりあえず頑張ってみます(^_^;)
まずは、「文字」「文字列」「配列」「ポインタ」について復習してみてください。
基礎的な関数ですので、自作できることよりも、仕組みを理解することが重要だと思います。
再度のご返答ありがとうございます。
No.8さんへのご返答にも書いたのですが、%s以外の変換指定がその値そのもののビットの並びをint型などに変換するのに対して、%sはその値のアドレスが指しているビットの並びを変換しているという部分が、少し疑問に感じてしまったんです。
いろいろとご教授くださってありがとうございました。
基礎的な部分にはなるのですが、これからも勉強してみます。
No.8
- 回答日時:
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』が特別だったわけではありません。
重要なのは『配列名はアドレス(ポインタ)を意味する』を理解していなかっただけです。
・以上。
ご返答ありがとうございます。
配列とポインタについては理解していたつもりだったんですけど(^_^;)
ただ、他の変換指定、例えば%dなどは、その値(ビットの並び)をint型に変換するものなのに対して、%sはその値をアドレスだと認識して、そのアドレスにある値を\0が出現するまで文字列として変換するという、他の変換指定とは違う動きをしている部分が疑問に感じてしまって。
それが、Cの規格としてどのように定義されているのかを知りたかったんです。
わかりにくい質問ですみませんでしたm(__)m
No.7
- 回答日時:
>そもそも C に「文字列」なんてものは存在しない
文字列も一応規格に用語が以下のように定義されています。
最初のナル文字で終わり、かつそれを含む連続した文字の並びを「文字列」という。
No.6
- 回答日時:
って~か, そもそも C に「文字列」なんてものは存在しない (いや, 規格には「文字列リテラル」ってあるけど...). あれは「'\0' で終わる文字の配列」だ.
で, 「配列名」は一部の場面を除いて全て「その配列の先頭要素をさすポインタ」に変換される.
だから, pointer = word; を実行すると pointer の値は「配列 word の先頭要素のアドレス」になる.
で, printf("%s\n", word); の printf の第2引数は配列名なので「その配列の先頭要素をさすポインタ」に変換される. つまり「配列 word の先頭要素のアドレス」である.
一方, printf("%s\n", pointer); における printf の第2引数は pointer であるが, その値は (上の代入により) 「配列 word の先頭要素のアドレス」である.
だから, この 2つは同じ結果でなければならない.
ちなみにポインタの値を %p 以外で変換するのは未定義動作なので, 「突然鼻歌を歌いだす」「どこかの原発をメルトダウンさせる」ということがあったとしても規格は一切関知しない.
配列名がその配列の先頭アドレスを指すことまではわかっていたんですけど、「"%s"がそのアドレスの指す値から\0が出てくるまでを文字列にして表示する」なんて処理をやってくださっているというのが、はっきりわかっていなくて。
今まで何気なく使っていた%sさんに感謝することにします(^^ゞ
ご返答ありがとうございました。
No.5
- 回答日時:
#ANo.4です。
自分の書き込みで何が言いたいか分かりにくかったので補足。。。
char str[]= "test";
char* pointer;
pointer = str;
printf ("%s", *pointer);
・上記の場合の「*pointer」は既にC規格からみて
「文字型配列の先頭要素へのポインタ」にはならない。
・精度を指定すれば、\0まで表示されない事もある。
(精度とはprintf( "%10s", str)などの10のこと)
などなど。
規格として、「%sはアドレスを受け取るもの」だったんですね。
今まで何となく使っていたんですけど、こうしてはっきりわかると安心しました。
ご返答、ありがとうございました。
No.3
- 回答日時:
そういうものです。
ためしにご自分で puts 関数を作ってみればよくお分かり頂けると思います。
内部で使っていい関数は putchar だけとし、stdout に対して出力してみてください。
(*pointer) の型は char 型なので、引数は char 型にして見ましょう。
その場合、どのように二文字目以降を表示すればよいのでしょうか。
実現できない事がお分かり頂けると思います。
やっぱりそうですよね。
%sは参考書などでかなり最初のほうから出てきますし、特に説明もないのであまり深く考えていなかったんですけど、かなり特殊な変換指定だったんですね。
これからは意識して使ってみることにします。
puts関数の自作はかなり難しそうな気がするんですけど、とりあえず頑張ってみます(^_^;)
ご返答ありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
このQ&Aを見た人はこんなQ&Aも見ています
-
夏が終わったと感じる瞬間って、どんな時?
まだまだ暑い今日この頃。 しかしながら、もう夏は終わっている!……はず。 あなたが思う「夏が終わった!」エピソードを教えてください。
-
人生最悪の忘れ物
今までの人生での「最悪の忘れ物」を教えてください。 私の「最悪の忘れ物」は「財布」です。
-
ホテルを選ぶとき、これだけは譲れない条件TOP3は?
ホテルを探す時、予約サイトで希望条件の絞り込みができる便利な世の中。 あなたは宿泊先を決めるとき「これだけは譲れない」と思う条件TOP3を教えてください。
-
【大喜利】【投稿~10/21(月)】買ったばかりの自転車を分解してひと言
【お題】 ・買ったばかりの自転車を分解してひと言
-
14歳の自分に衝撃の事実を告げてください
タイムマシンで14歳の自分のところに現れた未来のあなた。 衝撃的な事実を告げて自分に驚かせるとしたら何を告げますか?
-
charと%c , %s の関係について
C言語・C++・C#
-
init関数の意味
C言語・C++・C#
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・14歳の自分に衝撃の事実を告げてください
- ・架空の映画のネタバレレビュー
- ・「お昼の放送」の思い出
- ・昨日見た夢を教えて下さい
- ・ちょっと先の未来クイズ第4問
- ・【大喜利】【投稿~10/21(月)】買ったばかりの自転車を分解してひと言
- ・メモのコツを教えてください!
- ・CDの保有枚数を教えてください
- ・ホテルを選ぶとき、これだけは譲れない条件TOP3は?
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】
- ・ハマっている「お菓子」を教えて!
- ・最近、いつ泣きましたか?
- ・夏が終わったと感じる瞬間って、どんな時?
- ・10秒目をつむったら…
- ・人生のプチ美学を教えてください!!
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・都道府県穴埋めゲーム
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語 配列の長さの上限
-
先頭アドレスとは何ですか?
-
配列を使わずに、変数名を動的...
-
配列で格納したものをmsgboxで...
-
C# Listを使わずに2次元配列の...
-
C言語初心者 ポインタについて...
-
VBで構造体の配列を関数に渡す...
-
【C言語】配列の中に配列を入れ...
-
Directx パズドラ風パズルゲー...
-
VBでC言語のポインタみたい...
-
VB.NET 構造体の配列の検索機能...
-
C# 配列の変数宣言について。
-
配列の参照渡しで型が一致しま...
-
ExcelVBAで質問です。離れた二...
-
【VBS】 フォルダ直下のファイ...
-
複数の選択範囲の行番号を個別...
-
unsigned char配列への入力の仕方
-
配列内の文字間を排他的論理和...
-
メモリの初期値
-
char型の配列 char buff[20] = ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C言語 配列の長さの上限
-
配列を使わずに、変数名を動的...
-
配列で格納したものをmsgboxで...
-
C# 配列の変数宣言について。
-
C# Listを使わずに2次元配列の...
-
配列をEraseしてもメモリが開放...
-
unsigned char配列への入力の仕方
-
Redimした動的配列はEraseする...
-
配列の参照渡しで型が一致しま...
-
先頭アドレスとは何ですか?
-
【C言語】配列の中に配列を入れ...
-
ExcelVBAで質問です。離れた二...
-
複数の選択範囲の行番号を個別...
-
VBで構造体の配列を関数に渡す...
-
C言語で特定列だけを抽出して配...
-
配列を含む構造体の初期値について
-
【速いブラインドタッチ】手を...
-
テキストファイルから文字列を...
-
VBでC言語のポインタみたい...
-
メモリの初期値
おすすめ情報