web-dev-qa-db-ja.com

関数ポインタをフォーマットする方法は?

ANSI Cの関数へのポインタを出力する方法はありますか?もちろん、これは関数ポインタをvoidポインタにキャストする必要があることを意味しますが、それは不可能ですか?

#include <stdio.h>

int main() {
    int (*funcptr)() = main;

    printf("%p\n", (void* )funcptr);
    printf("%p\n", (void* )main);

    return 0;
}

$ gcc -ansi -pedantic -Wall test.c -o test
test.c:関数 'main':
test.c:6:警告:ISO Cは、関数ポインターのオブジェクトポインター型への変換を禁止しています
test.c:7:警告:ISO Cは、関数ポインターのオブジェクトポインター型への変換を禁止しています
$ ./テスト
0x400518
0x400518

それは「機能」していますが、非標準です...

これを行う唯一の正当な方法は、文字型を使用してポインタを構成するバイトにアクセスすることです。このような:

#include <stdio.h>

int main() {
    int (*funcptr)() = main;
    unsigned char *p = (unsigned char *)&funcptr;
    size_t i;

    for (i = 0; i < sizeof funcptr; i++)
    {
        printf("%02x ", p[i]);
    }
    putchar('\n');

    return 0;
}

dreamlaxの答え のように、関数ポインターをvoid *、または非文字型としてパンニングすることは、未定義の動作です。

関数ポインタを実際に構成しているバイトの意味は、実装によって異なります。たとえば、関数のテーブルへのインデックスを表すだけです。

45
caf

警告/エラーを回避できる共用体の使用がありますが、結果は(おそらく)未定義の動作です:

_#include <stdio.h>

int
main (void)
{
  union
  {
    int (*funcptr) (void);
    void *objptr;
  } u;
  u.funcptr = main;

  printf ("%p\n", u.objptr);

  return 0;
}
_

Ifステートメントを使用して2つの関数ポインター(例:printf ("%i\n", (main == funcptr));)を比較して、それらが等しいかどうかをテストできます(私はその目的が完全に無効であり、無関係である可能性が非常に高いことを知っています)。関数ポインタのアドレスを出力する場合、何が起こるかはターゲットプラットフォームのCライブラリのベンダーとコンパイラ次第です。

5
Dustin

関数ポインタを整数にキャストしてから、もう一度ポインタにキャストして "%p"を使用してください。

#include <stdio.h>

int main() {
    int (*funcptr)() = main;

    printf("%p\n", (void *)(size_t) funcptr);
    printf("%p\n", (void *)(size_t) main);

    return 0;
}

一部のプラットフォーム(「中」または「コンパクト」メモリモデルの16ビットDOSなど)では、データへのポインタと関数へのポインタは同じサイズではありません。

2
dan04

Gcc 4.3.4では、スイッチ-O2 -Wstrict-aliasingを使用すると、dreamlaxの答えは次のようになります。

warning: dereferencing type-punned pointer will break strict-aliasing rules

追加:endiannessに関するcafの回答への反対は、妥当であると思います。ソリューションは対処しません(しゃれは意図されていません)。ダスティンのユニオンキャストの使用に関する提案はおそらく合法です(私が読んだことからいくつかの議論があるようですが、yourコンパイラは法律より重要です)。しかし、彼のコードは1行で簡略化(または、好みによっては難読化)できます。

printf("%p\n", ((union {int (*from)(void); void *to;})funcptr).to);

これにより、gccの厳密なエイリアスの警告が削除されます(ただし、「正しい」ですか?)。

-pedanticスイッチを使用している場合、または例として使用している場合、集約キャストは「機能しません」。 SGI IRIXなので、以下を使用する必要があります。

printf("%p\n", ((union {int (*from)(void); void *to;} *)&funcptr)->to);

しかし、元の質問に関しては、その起源は-pedanticの使用にあります。

さらに編集:最後の例ではmainを使用できないことに注意してください。

printf("%p\n", ((union {int (*from)(void); void *to;})   main).to);  // ok
printf("%p\n", ((union {int (*from)(void); void *to;} *)&main)->to); // wrong!

もちろん&mainmainに崩壊します。

1
Joseph Quinsey

これを試して:

#include <stdio.h>
#include <inttypes.h>


int main() {
    int (*funcptr)() = main;
    unsigned char *p = (unsigned char *)&funcptr;
    int i;

    /* sample output: 00000000004005e0 */
    printf("%016"PRIxPTR"\n", (uintptr_t)main);
    /* sample output: 00000000004005e0 */
    printf("%016"PRIxPTR"\n", (uintptr_t)funcptr);

    /* reflects the fact that this program is running on little-endian machine
    sample output: e0 05 40 00 00 00 00 00 */
    for (i = 0; i < sizeof funcptr; i++)
    {
        printf("%02x ", p[i]);
    }
    putchar('\n');

    return 0;
}

このフラグを使用:

gcc -ansi -pedantic -Wall -O2 -Wstrict-aliasing c.c

これらのフラグを使用しても警告は出されません

1
Michael Buen

これがコーシャかどうかはわかりませんが、ループ、ユニオン、非ポインタ型へのキャスト、またはstring.h以外の追加の依存関係なしで効果を達成します

int (*funcptr)() = main;
void* p = NULL;
memcpy(&p, (void**) &funcptr, sizeof(funcptr));
printf("%p", p);

GCC 7.1.1ではgcc -ansi -pedantic -Wall -Wstrict-aliasingの警告はありません

0
Jon Deaton