ホーム
TOPへ
最終更新日:2006年 2月 8日
Index
● [VC++] C 言語の関数ポインタの渡し方(Microsoft Visual C++ 6.0)
Contents
● [VC++] C 言語の関数ポインタの渡し方(Microsoft Visual C++ 6.0)
あるCの関数に関数ポインタを引数として渡すとき、仮引数として定義
されているものとは関数型や引数の型・数が異なっている場合、例えば
void check( void (*fnc)(int, short) ) ;
int check1( int a, short b ) ;
のような関数が存在して、check() の引数として check1() へのポインタ
を渡そうとするとき、なにも考えずに
check( (void *)check1 ) ;
のようにコールしてしまうと、VC++ではコンパイルエラーが発生してしまう。
※ちなみに、GCC だとこれでも通る。
【エラーメッセージ】
C:\TEMP\pnt2fnc\pnt2fnc.cpp(53) : error C2664: 'check' : 1 番目の
引数を 'void *' から 'void (__cdecl *)(int,short)' に変換できません。
(新しい機能 ; ヘルプを参照)
'void*' から非 'void' 型への変換には明示的なキャストが必要です。
色々と試してみたところ、正式な(?)キャスト方法は、
check( (void (*)(int, short))check1 ) ;
であるらしい。
言われてみれば、なるほどである。
ちなみに、
void check( int (*fnc)(int, short) ) ;
char check1( int a, short b ) ;
のような場合には、
check( (int *)check1 ) ;
では VC++ / GCC に関わらずコンパイルエラーとなる。
仮引数の定義はあくまで戻り値がintの関数へのポインタであって、
int型変数へのポインタではないので、当然といえば当然である。
きちんと、
check( (int (*)(int, short))check1 ) ;
のようにしなければいけない。
ネットを検索してみると、
check( (void (__fastcall *)(int, short))check1 ) ;
だと VC++ で OK。
GCC 等との互換性を考慮して、
#ifdef WIN32
# define CALLBACK __fastcall
#else /* WIN32 */
# define CALLBACK
#endif /* WIN32 */
check( (void (CALLBACK *)(int, short))check1 ) ;
のようにした方がベター。
のような書かれ方をしているところもあるが、これは自分の環境ではエラー
となった(__fastcallではなく、__cdeclならば通った)。
環境により呼び出し規約(*1)が異なるため、環境に応じてマクロを変更する
必要がある。
(*1)__fastcall は呼出規約のひとつということ。これは、
『プロジェクト(P)』→『設定(S)』→『C/C++』タブ
→『カテゴリ(Y)』において、"コード生成"選択→『呼び出し規約(C)』
で確認及び変更が可能。
呼び出し規約 コンパイルオプション
------------ --------------------
__cdecl * /Gd(デフォルト)
__fastcall /Gr
__stdcall /Gz
以下、サンプルソース(pnt2fnc.c)。
※引数と戻り値が異なる 4 種類の関数 check1()〜check4() へのポインタを
引数として check() 関数をコールし、check()内で 1〜4をコールしている。
===[ここから]=============================================================
#include <stdio.h>
#if 0
// /* --- CALLBACK マクロ定義(→実は必要なさそう) --- */
// /* ※ __cdecl は呼び出し規約に応じて変更 */
//
// #ifdef WIN32 /* VC++ では __cdecl をつける */
// # define CALLBACK __cdecl
// #else /* WIN32 */ /* GCC では __cdecl をつけない */
// # define CALLBACK
// #endif /* WIN32 */
#endif
/* --- プロトタイプ宣言 --- */
void check( int (*fnc)(int, short) ) ;
void check1( int a ) ;
int check2( int a, short b ) ;
char check3( short a, char b, int c ) ;
long check4( int a, short b, char c, long d ) ;
/* --- 関数定義 --- */
void check( int (*fnc)(int, short) )
{
long rcode ;
static int a=32768 ;
static short b=128 ;
printf( "--- check start\n" ) ;
rcode = (*fnc)( a, b ) ;
printf( "< rcode = %ld(0x%08lX) >\n", rcode, rcode ) ;
printf( "--- check end\n\n" ) ;
}
void check1( int a )
{
printf( "check1: a=%d\n", a ) ;
} /* end of check1() */
int check2( int a, short b )
{
printf( "check2: a=%d b=%d\n", a, b ) ;
return( 0 ) ;
} /* end of check2() */
char check3( short a, char b, int c )
{
printf( "check3: a=%d b=%d c=%d\n", a, b, c ) ;
return( 0 ) ;
} /* end of check3() */
long check4( int a, short b, char c, long d )
{
printf( "check4: a=%d b=%d c=%d d=%ld\n", a, b, c, d ) ;
return( 0 ) ;
} /* end of check4() */
/* --- メイン関数 --- */
int main( void )
{
int ret = 0 ;
for (;;) {
#if 1 /* とりあえずこれで動作する */
check( (int (*)(int,short))check1 ) ;
check( (int (*)(int,short))check2 ) ;
check( (int (*)(int,short))check3 ) ;
check( (int (*)(int,short))check4 ) ;
#else /* わざわざ CALLBACK を定義する必要もなさそう */
// check( (int (CALLBACK *)(int,short))check1 ) ;
// check( (int (CALLBACK *)(int,short))check2 ) ;
// check( (int (CALLBACK *)(int,short))check3 ) ;
// check( (int (CALLBACK *)(int,short))check4 ) ;
#endif
break ;
}
return( ret ) ;
} /* end of main() */
===[ここまで]=============================================================
Win32 Console Applicationプロジェクトによる処理結果は以下の通り。
===[ここから]=============================================================
--- check start
check1: a=32768
< rcode = 16(0x00000010) >
--- check end
--- check start
check2: a=32768 b=128
< rcode = 0(0x00000000) >
--- check end
--- check start
check3: a=-32768 b=-128 c=1245056
< rcode = 0(0x00000000) >
--- check end
--- check start
check4: a=32768 b=128 c=-128 d=1243068
< rcode = 0(0x00000000) >
--- check end
===[ここまで]=============================================================
Indexに戻る
ホーム
このページの先頭