wsprintf や、 printf などで、引数がいくらでもよい関数があります。
あれはどうやって宣言するのでしょうか?
WINUSER.Hでは、
WINUSERAPI int WINAPIV wsprintfA(LPSTR, LPCSTR, ...);
このように定義されています。最後の...のようにして
引数を受け取るそうです。本当にこのようにして、引数を受け取ることが
できるのでしょうか?そして、これはプロトタイプ宣言ですが、本当の
関数の中では、どうやって引数や引数の数などを取得するのでしょうか?
不思議です.....(謎)
#なーつやすみ♪なーつやすみ♪
Cの関数は引数を右から左へ順にスタックに積んでゆきます。
つまりスタックの一番上には第1引数がくることになります。
関数終了時のスタックの消去は呼び出し側が行います。
printf の場合は第1引数に
sprinf や fprintf の場合は第2引数に
関数が必要とする引数の数や型の情報を指定します。
この場合は書式指定文字中の "%d" や "%s" などがそれです。
つまりコンパイル時には引数の数や型は感知できないのです。
このため、引数の数や型を間違った場合は実行時までエラーが判定できません。
一方、通常のWin32 API は関数は引数を左から右へスタックし、
スタックの消去は関数側が行います。
そして、Win32 API のおそらく唯一の例外が wsprintf で、これだけは
Cの関数と同じ方法をとっています。
さて、C++の場合は後者の方法を基本にして、プロトタイプ宣言で ... が
指定されたときだけ、前者の方法に切り替わります。
> wsprintf や、 printf などで、引数がいくらでもよい関数があります。
> あれはどうやって宣言するのでしょうか?
こんな感じでOKです。
int foo(int i引数の数, ...);
> できるのでしょうか?そして、これはプロトタイプ宣言ですが、本当の
> 関数の中では、どうやって引数や引数の数などを取得するのでしょうか?
以前はスタックの中身を意識しながらポインタやキャストを駆使して
コーディングしてましたが、現在は
va_arg、va_end、va_start といったマクロを使用できるそうです。
可変データの引渡しは、現実には配列へのポインタを使用すること
が多いのであまり使いませんね。
---------------------------------
Ishikawa Hirotoshi @ Funabashi
mailto:phil...@mvc.biglobe.ne.jp
http://www2d.biglobe.ne.jp/~Philopon/
---------------------------------
WINAPI = stdcall
WINAPIV = cdecl
だからね。WINAPIV や APIENTRYV (だったかな?) で検索したら、他には
Video For Windows あたりが使っていたように記憶してるけど。
最近は(っていうほど最近ではないか) Shell Lightweight APIs なんて
作り出したから、そのうち WINAPIV で宣言されたものも増えるのじゃない
かな。
Shell Lightweight APIs には
int wnsprintf( LPTSTR lpOut, int cchLimitIn, LPCTSTR pszFmt, ...);
なんてのもありますしね。
# wsprintf 作ったときに一緒に作っとけよ
"Ishikawa Hirotoshi" <phil...@mvc.biglobe.ne.jp> writes:
> Cの関数は引数を右から左へ順にスタックに積んでゆきます。
> つまりスタックの一番上には第1引数がくることになります。
> 関数終了時のスタックの消去は呼び出し側が行います。
Cではそのような決まりはありません。
たんにそういう実装が多いというだけです。
> 一方、通常のWin32 API は関数は引数を左から右へスタックし、
> スタックの消去は関数側が行います。
> そして、Win32 API のおそらく唯一の例外が wsprintf で、これだけは
> Cの関数と同じ方法をとっています。
この所はWin32APIと書かれていてWindowsに限定してるのでいいのですけど
> さて、C++の場合は後者の方法を基本にして、プロトタイプ宣言で ... が
> 指定されたときだけ、前者の方法に切り替わります。
C++とすると、これもC++では決められていません。
Windowsに限定したとしても実際には関数への引数の渡し方はcdecl,stdcall,
fastcallなどで変わってきます。
ただしこれも処理系依存です。
> > できるのでしょうか?そして、これはプロトタイプ宣言ですが、本当の
> > 関数の中では、どうやって引数や引数の数などを取得するのでしょうか?
> 以前はスタックの中身を意識しながらポインタやキャストを駆使して
> コーディングしてましたが、現在は
> va_arg、va_end、va_start といったマクロを使用できるそうです。
ANSI Cではstdarg.hでそれらの定義がされましたけど、それ以前には同様なマ
クロ(同じではありません)がvarargs.hで定義されてたと思うのですが、それ
は使われなかったのでしょうか?
少なくとも私はCでそのような関数を書く時にはスタックを意識した事はない
です。
--
okabe katsuyuki <hgc0...@nifty.ne.jp>
http://member.nifty.ne.jp/wills/
> たんにそういう実装が多いというだけです。
おっしゃるとおり。
fj.os.ms-windows.programming で購読してたので
MS Visual C++ を前提で回答してしまいました。
> ANSI Cではstdarg.hでそれらの定義がされましたけど、それ以前には同様なマ
> クロ(同じではありません)がvarargs.hで定義されてたと思うのですが、それ
> は使われなかったのでしょうか?
DOS 時代の古い話です。
なかったか、あってもとても使えるものではなかったと記憶しておりますが、
処理系依存の話です。
はなしはそれますが、DOS 時代、特に初期のDOSのCの処理系って、
今から見るととんでもないものが多かった。
なんせ、コンパイルできるだけですごいって世界でしたから(^^;
標準ヘッダファイルの stdarg.h にある機能(マクロ)を使います。
スタックがどうだとかといったことは、普通は考える必要はありません。
具体的な方法は、C言語 FAQ (http://www.catnet.ne.jp/kouno/c_faq/c_faq.html)
などを参照にしてください。
# ちなみに私は、LSI-C 86 のライブラリリファレンスで stdarg.h の
# 使い方を知りました。ほとんどのライブラリリファレンスにも解説が
# あると思います。
---------------------------------
宮坂 賢 (Miyasaka, Masaru)
Asahikawa-City, Hokkaido, Japan
e-mail : alk...@coral.ocn.ne.jp
"Ishikawa Hirotoshi" <phil...@mvc.biglobe.ne.jp> writes:
> > ANSI Cではstdarg.hでそれらの定義がされましたけど、それ以前には同様なマ
> > クロ(同じではありません)がvarargs.hで定義されてたと思うのですが、それ
> > は使われなかったのでしょうか?
> DOS 時代の古い話です。
> なかったか、あってもとても使えるものではなかったと記憶しておりますが、
> 処理系依存の話です。
かなり昔の話なんですかね?
私の知ってるのはせいぜいMS-C6あたりからだし。