• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:printfの引数指定でなぜ文字列ポインタがOK?)

printf関数の引数指定について

このQ&Aのポイント
  • C言語において、printf関数の引数指定について疑問があります。
  • printf関数では、書式文字列または文字列の先頭アドレスを指定することができます。
  • 文字列の場合、宣言時にchar配列として宣言し、先頭のアドレスを使って文字列を扱うことが一般的です。

質問者が選んだベストアンサー

  • ベストアンサー
  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.3

厳密に言うと、Cには「文字列型」って無いんです。 char型は「文字コードが一つ入る大きさを持った整数」です。 'a'(シングルクオート)は「文字aのコードに相当する整数」です。 文字列は「char型へのポインタを先頭にして、以降『0』になるまでの間にあるchar型の数値の連続」を「文字列」として扱う、という決まりになっています。 "ABC"というのは 'A','B','C',0と連続したchar型の列への先頭アドレス、ということになります。 また、Cでは、ポインタと配列は同列に扱われます。 このあたりの振舞いや、配列とポインタについては、それだけで本があるくらい、Cでは重要なことです。しっかり理解するようにしましょう。 VBでは「文字」と「文字列」が区別が無い、とありますが、正確には「文字」は「長さ1の文字列」なので、違いが無いのも当りまえのことです。 さて、それを踏まえると >  printf(書式文字列 または 文字列の先頭アドレス, 変数) 書式文字列も「文字列」ですから、指定する際にはその先頭アドレスを使用します。なので、printfは単に printf(文字列の先頭アドレス[, ...]) となります。([, ...]は省略可能な値) 書式文字列では、 %で始まる変換書式以外はそのまま出力されます。よって、今回の場合はprintf("%s",b)とprintf(b)が同じ出力になります。 > char a = 'A'; > printf(a); がエラーになるのは、aが数値であってポインタでは無いからです。

palox0505
質問者

補足

丁寧な回答ありがとうございます。 >このあたりの振舞いや、配列とポインタについては、それだけで本があるくらい、Cでは重要なことです。しっかり理解するようにしましょう。 重要ポイントですね。がんばります。 >VBでは「文字」と「文字列」が区別が無い、とありますが、正確には「文字」は「長さ1の文字列」なので、違いが無いのも当りまえのことです。 そういうことなんですね。 C言語を勉強し始めて思ったのですが、VBのことにも理解が深まっていきます。 >> char a = 'A'; >> printf(a); > >がエラーになるのは、aが数値であってポインタでは無いからです。 わかりやすいです! ここで、ちょっと疑問が出てきたのですが、 文字のポインタでprintfに渡すと、  char a = 'A';  printf(&a); →「AフフフフH・」と出力されたのですが、これはどういうことなのでしょうか? 文字列の先頭アドレスがprintfに渡されるので無理矢理 >以降『0』になるまでの間・・・ に続いているアドレスに格納されていた文字が出力されたのでしょうか? さらに、  int a = 9;  printf(&a); ではいくつかの空白が出力され、  int a = 47;  printf(&a); では「/」が出力されたのですが、 まず「9」については「9」というコードに相当する文字が空白で、移行「0」になるまで空白が格納されていた。 「47」については、「/」のコードが「47」で、すぐ後のアドレスに「0」があった。 ということでしょうか?

その他の回答 (5)

  • titokani
  • ベストアンサー率19% (341/1726)
回答No.6

>ということは、printf関数の引数の指定方法は > printf(書式文字列 または 文字列の先頭アドレス, 変数) >というように考えられるのですが、認識があっているでしょうか? もうわかってらっしゃるかもしれませんが、これは違います。 char a[] = "ABC"; printf(a); は、「ABC」と出力されますが、 char a[] = "A%sC"; printf(a); だと、実行時エラーになるでしょう。 これが、 char a[] = "A%sC"; printf("%s",a); だと、「A%sC」と出力されます。 ついでに、 char a[] = "A%sC"; printf(a,"X"); だと、「AXC」ですね。 つまり、第一引数は常に書式文字列ということですね。

palox0505
質問者

お礼

titokaniさんありがとうございます。 >つまり、第一引数は常に書式文字列ということですね。 >printf(書式文字列 または 文字列の先頭アドレス, 変数)」 で「または」としているのがおかしいですね。 第一引数は「文字列」の先頭アドレスで、「文字列」の内容は書式文字列として指定する。 ですね。 「文字列」の先頭アドレスでないとコンパイルエラー 「文字列」内に%sなどのフォーマット指定子を指定した場合は、続けて対応する引数を指定しないと、実行時エラーになる。 とまとめてみたのですがいかがでしょう。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.5

余談ですが, 文字列リテラルは実際には無名配列になります. つまり #include <stdio.h> int main() { printf("Hello, world\n"); return 0; } と書いた場合, 実質的に #include <stdio.h> int main() { static const char _[] = "Hello, world\n"; printf(_); return 0; } と等価 (ただし配列名は使えない) です. もちろん #include <stdio.h> int main() { static const char _[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '\n', '\0' }; printf(_); return 0; } とも (ほぼ) 等価になります.

palox0505
質問者

お礼

Tacosanさんありがとうございます。 なるほどですね。 変数としては使えない状況にあるが、メモリ上では同じように扱われるということですね。 理解が深まります。

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.4

>  char a = 'A'; >  printf(&a); > →「AフフフフH・」と出力されたのですが、これはどういうことなのでしょうか? > 文字列の先頭アドレスがprintfに渡されるので無理矢理 >> 以降『0』になるまでの間・・・ > に続いているアドレスに格納されていた文字が出力されたのでしょうか? そういうことです。 これもCの特徴なのですが ・明示しない限り、変数やメモリの初期化はしない。したがって、そこに何が書かれているかは保証されない。 今回はたまたまchar aの後にフフフHと読める領域が続いていたのでそう表示された ・配列とかポインタの範囲のチェックはしない VBではdim a(10)としたら a(20)などではエラーになると思います。しかし、Cではa[10]としてもa[20]はコンパイルエラーになりません。実際に動作させても、たまたまエラーにならないこともあります。 > int a = 9; > printf(&a); > ではいくつかの空白が出力され、 > まず「9」については「9」というコードに相当する文字が空白で、移行「0」になるまで空白が格納されていた。 ポインタの「型」は、実体にアクセスするときに、何バイト取りだして、どんな処理をするか、を決めるためのものです。内部的にはただのアドレスであることが多いです。 ここからは、実行環境に依存する話です。 まず、intはsizeof(int)バイト分のメモリを使用します。intが32bitなら4バイトです。 その範囲でどのような順番に並んでいるか、も違います。現在主流のIntel系CPUだと、リトルエンディアンといて、下のバイトが先にくるように並んでいます。つまり、intの 9はメモリ上に 9,0,0,0 と4バイト並んでいることになります。 int型へのポインタの場合、その参照の際には4バイトをセットにして扱いますので、参照した値は「9」になります。 ところが、printfの第一引数では、const char *なので、先頭アドレスは同じでも、各バイト毎に扱うので、 長さ1の文字列で、1文字目のコードが9、と解釈されます。 ここで、やはり現在主流のASCIIコード互換の文字コードを考えると、 0~31は画面には文字を表示せずに特別な意味を持つコントロール文字と呼ばれるものになっています。 9はタブ、または水平タブと呼ばれるもので、現在のカーソル位置から「タブストップ」と呼ばれる位置(通常8文字単位)までカーソルを移動させます。画面上は空白がいくつか並んでいるように見えます。 > 「47」については、「/」のコードが「47」で、すぐ後のアドレスに「0」があった。 同様に 47,0,0,0 とメモリ上にあるので「長さ1の文字列で1文字目が「/」」と解釈されています。

palox0505
質問者

お礼

すっきりしました! 実に明快な回答をありがとうございます! >これもCの特徴なのですが >・明示しない限り、変数やメモリの初期化はしない。したがって、そこに何が書かれているかは保証されない。 >今回はたまたまchar aの後にフフフHと読める領域が続いていたのでそう表示された >・配列とかポインタの範囲のチェックはしない >VBではdim a(10)としたら a(20)などではエラーになると思います。しかし、Cではa[10]としてもa[20]は >コンパイルエラーになりません。実際に動作させても、たまたまエラーにならないこともあります。 この辺がCが難しくもあるが単純明快でもあるという理由なのかな?という感じがしました。 Cの魅力を垣間見れた気がします。 勉強を進めていきます!

noname#140045
noname#140045
回答No.2

>printf(書式文字列 または 文字列の先頭アドレス, 変数)…認識があっているでしょうか? 合っていると言えば、合っています。 が、もっと正確に言えば、printf()で最初の引数は「文字列の先頭アドレス」であって、書式文字列も、それは文字列の先頭アドレスの1種に過ぎないのです。 (書式文字列の実体は別に領域がとられ、その書式文字列の先頭アドレスが引き渡される) また、C言語では「文字列」であるかどうかが問題であって、そこに書式が定義されているかは問題ではありません。 つまり、 printf("%s"); などとすれば、コンパイルエラーとはなりませんが、実行時に大抵は異常終了します。

palox0505
質問者

補足

みなさん早くに回答恐縮です。 ありがとうございます。 この問題はprintfの引数がどうたらというより、C言語における文字列の問題ですね・・・ >が、もっと正確に言えば、printf()で最初の引数は「文字列の先頭アドレス」であって、書式文字列も、それは文字列の先頭アドレスの1種に過ぎないのです。 >また、C言語では「文字列」であるかどうかが問題であって、そこに書式が定義されているかは問題ではありません。 わかってきたような気がします。 書式文字列 または 文字列の先頭アドレス・・・はどちらも同じで、文字列の先頭アドレスなんですね。 >つまり、 >printf("%s"); >などとすれば、コンパイルエラーとはなりませんが、実行時に大抵は異常終了します。 これは一応文字列が指定された形にはなっているのでコンパイル時にエラーとはならないが、 実行時に、実際何を%sにするの?何もないじゃん。ということでエラーとなり異常終了する。 ということでしょうか?

  • koko_u_u
  • ベストアンサー率18% (216/1139)
回答No.1

> 「printf(a)」=「printf(b)」=「printf("ABC")」ということになりますが、 そうです。 つまりコンパイラには "ABC" という「文字列」がその先頭アドレスのように見えている、ということです。 printf() のプロトタイプ宣言を確認しましょう。

palox0505
質問者

お礼

こんなに早く回答が頂けるとは! ありがとうございます! >> 「printf(a)」=「printf(b)」=「printf("ABC")」ということになりますが、 > >そうです。 >つまりコンパイラには "ABC" という「文字列」がその先頭アドレスのように見えている、ということで。す。 なるほど、コンパイラは文字列を先頭アドレスで扱うんですね? >printf() のプロトタイプ宣言を確認しましょう。 まだヘルプとかの見方が慣れなくて・・・勉強します。