2005年09月20日

printfとscanfでなぜフォーマットが違うのか

printfのときはdoubleもfloatも%fなのにscanfは%lfと%fになるという話です.

この問題を考えるとき,まず,printfやscanfがどういう関数かというのを確認しておく必要があります.
つまり,これらの関数は可変引数であり,渡す引数の数や型が決まっていないということです.

もうちょっと詳しく考えると,printfは,引数としてさまざまな型の数字をとり,それをフォーマットに従って表示します.
一方scanfは,さまざまな型の変数のポインタをとり,そこに,フォーマットにしたがって値を入れていきます.

そもそも,関数に引数を渡すということは,値をスタックに積むということです.
そして,C言語の場合はそれだけです.
スタックに積んである値には型の情報もどこまでが引数かなんていう情報もありません.
よって,それをどうにかして知る必要があります.それがprintfやscanfにおけるフォーマット文字列なわけです.

ということはprintfでも%cやら%hdが必要やん,という気もするのですが,ここでクローズアップされるのが「引数省略時の型の格上げ」です.

可変引数の場合,intより小さい整数型はintに,floatはdoubleに自動格上げされます.
変数で値を渡すときはどうかんがえても迷惑千万な仕様ですが,リテラルで渡すときはなかなか型を意識しづらいですからやっぱりありがたい仕様なんですね.

そういうことで,printfには,charやshortで値を渡してもprintfにはintに見えてます.
だから整数は%d,小数は%fで済ませられるわけです.
でも,整数はintが基準なのでlongとかlong longの時はちゃんと%ldや%lldを使いましょう.
こんなになっちゃうという例(32bitCPU用)
#include <stdio.h>

int main(void)
{
long long int a = 0xdead1234beef5678;
printf("%x %x\n",a);
return 0;
}

こう書くとprintfには%hdや%hhdがないように聞こえますが,これらはちゃんと存在します.
%dはcharもshortもなんでもござれなんですが,出力を制限するという意味で存在します.
printfに渡った時点では既にintに変換されているため,上の例みたいことは起こりません.これはこれで注意が必要.
#include <stdio.h>

int main(void)
{
int b = 0xdeadbeef;
printf("%hhx %hhx %hhx %hhx\n",b);
printf("%hx %hx\n",b);
printf("%x\n",b);
return 0;
}

さて,渡される引数の型という観点でいえば,scanfの方が楽です.
scanfに渡されるのはポインタですから全部同じ型です.
が,ポインタであるため,scanfからすればそれが何を指しているのかわかりません.
ですから,フォーマットの方で各型ごとに厳密に指定する必要があります.
scanfにとってフォーマットとは,読み込む文字列解析の手がかりであると同時に,
それを渡す変数領域の手がかりでもあるわけです.
/* scanfはサンプル書くのが面倒なのでパス:-) */

こういうことは,普段printf,scanfを使うときはなんとなくで過ごしてしまっていますが,いざ自分で可変引数の関数を作ろうとするとかなりはまるポイントなので注意しましょう.
posted by tak5219 at 04:35| Comment(2) | TrackBack(0) | C言語 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
うーん。

僕にはまだ難しいですね。

わからない英字がいっぱいでてきます。

でも、printfもscanfもかなりのことができる

関数だということがわかりました。

応用範囲が広いという捉え方でよいでしょうか?

スミマセン。

超初心者なもので、せっかくの詳しい説明が僕にはまるで呪文のようなのです。。。
Posted by roka at 2005年09月20日 06:34
printfやscanfは使い勝手がいいのでC言語使いはじめのころから良く使う関数なのですが,printf,scanf自体はなかなか難しくて複雑な関数です,という程度の話です.
というか,トラックバックしておいてなんですが,これはほとんど自分向けのメモであんまり人のこと考えてません^^;
Posted by tak5219 at 2005年09月20日 13:06
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。