ホーム
 TOPへ 最終更新日:2006年 2月 8日



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に戻る



ホーム  このページの先頭