4

C関数を交換できるかどうか誰かが知っているかどうかを探しています...?

 void swap2(int(*a)(int), int(*b)(int)) {
   int(*temp)(int) = a;
   *a = *b;
   *b = temp;
   // Gives 'Non-object type 'int (int)' is not assignable
 }

 swap2(&funcA, &funcB);

編集

意図に関する詳細データ -を使用して関数 ptr を作成し、それらを関数にポイントし、それらを切り替えるなどの作業を行うtypedefいくつかの回答が以下に提供されています。これにより、新しいスワップされた ptr を正常に呼び出すことができます。

ただし、スワップ後に元の名前で関数を呼び出しても、変更はありません。基本的に、objc「swizzle」に相当する ac を探しています。

cにはリフレクションが完全にないため、これは不可能であり、バイナリ自体を実際に変更する必要があると思い始めています(明らかに実現不可能です)。D:

コメント歓迎。

4

5 に答える 5

9

以下のような関数ポインタを使用する場合は、はい

typedef int (*func_pt)(int);

func_pt a, b;

void swap(func_pt * a, func_pt * b)
{
    func_pt tmp = *b;
    *b = *a;
    *a = tmp;
}

swap(&a, &b);

または、次のように使用します。いいえだと思います。

int test1(int a)
{
    return a;
}

int test2(int b)
{
    return b;
}

swap(&test1, &test2);

作業プログラムの完全なコンパイル

#include <stdio.h>
#include <stdlib.h>

typedef int (* func_pt)(int);

func_pt a, b;

int test1(int a)
{
    printf("test1\n");
    return 1;
}

int test2(int a)
{
    printf("test2\n");
    return 2;
}

void swap(func_pt * a, func_pt * b)
{
    func_pt tmp = *b;

    *b = *a;
    *a = tmp;
}

int main(void)
{
    a = &test1;
    b = &test2;

    printf("before\n");
    a(1);
    b(1);

    swap(&a, &b);

    printf("after\n");
    a(1);
    b(2);

    return 0;

}

出力:

before
test1
test2
after
test2
test1

ばかげていると言うだけで、自分で試してみない人もいるので、例を挙げます。

于 2013-05-16T05:42:44.367 に答える
3

ポインターを交換するには、関数ポインターへのポインターが必要だと確信していますね。このタイプのスワッピング関数は、値を交換します。あなたは本当にアドレスを扱いたいのです。C は関数をファーストクラスの変数として扱わないため、実際には関数を直接交換することはできません。アドレスは交換できるため、関数アドレスへのポインターを使用する必要があります。

void swap2(int(**a)(int), int(**b)(int)) {
   int(*temp)(int) = *a;
   *a = *b;
   *b = *temp;
}

int(*func1)(int) = &foo;
int(*func2)(int) = &bar;

swap2(&func1, &func2);
于 2013-05-16T05:43:59.230 に答える
1

はい、できます。関数ポインタは単なるメモリアドレスであると考えてください.1つの要件は次のとおりです。そのようなアドレスを保持する場所は変更可能である必要があります. つまり、int (*foo)()実際にはどこfooを指しているわけではありません。printf()またはの場合がありfopen()ます。

于 2013-05-18T17:46:28.830 に答える
0

主題はスワッピング機能について尋ねますが、実際には何が機能するかをエミュレートしたいと考えてswizzleいます。これは、同じ関数名を呼び出して別のことをさせたいということを意味します。

ポインターのみのソリューションでは、その動作は得られません。それが重要でない場合は、提供されている関数ポインターのみのソリューションのいずれかを採用する必要があります。それ重要な場合は、抽象化のレイヤーを導入する必要があります。抽象化では、内部で関数ポインターを使用できます (ただし、他の解決策があります)。

このインターフェースのユーザーへの API は次のようになります。

/* API to initialize */
void abstract_func_init ();

/* API to manipulate abstract functions */
typedef int abstract_func_type ();
abstract_func_type * abstract_func_get (abstract_func_type *key);
int abstract_func_set (abstract_func_type *key, abstract_func_type *behavior);

/* the abstract functions */
extern int foo ();
extern int bar ();

このようなインターフェースの実装は次のようになります。

static void insert (abstract_func_type *key, abstract_func_type **behavior)
{ /* associate key to behavior */ }
static abstract_func_type ** lookup (abstract_func_type *key)
{ /* return behavior from key */ }

abstract_func_type * abstract_func_get (abstract_func_type *k) {
    abstract_func_type **f = lookup(k);
    if (f) return *f;
    return 0;
}

int abstract_func_set (abstract_func_type *k, abstract_func_type *p) {
    abstract_func_type **f = lookup(k);
    if (f) {
        *f = p;
        return 0;
    }
    return -ENOENT;
}

#define DEFINE_ABSTRACT_FUNC(func) \
    static int static_##func (); \
    static abstract_func_type *func##_ptr = static_##func; \
    int func () { return func##_ptr(); } \
    static int static_##func ()

DEFINE_ABSTRACT_FUNC(foo) { return puts("foo"); }
DEFINE_ABSTRACT_FUNC(bar) { return puts("bar"); }

void abstract_func_init () {
    insert(foo, &foo_ptr);
    insert(bar, &bar_ptr);
}

次に、swap()投稿で最初に提示したものは、次のように実装できます。

void swap (abstract_func_type *a, abstract_func_type *b) {
    abstract_func_type *ap = abstract_func_get(a);
    abstract_func_type *bp = abstract_func_get(b);
    abstract_func_set(a, bp);
    abstract_func_set(b, ap);
}

を呼び出すプログラムは次のswap()とおりです。

    puts("before swap");
    foo();
    bar();
    swap(foo, bar);
    puts("after swap");
    foo();
    bar();

そして、その出力は次のようになります。

before swap
foo
bar
after swap
bar
foo

ルックアップ テーブルへの抽象関数の追加を自動化するには、ビルド システムに、行を出力するスクリプトを呼び出す追加のステップを導入し、grepそのような行ごとDEFINE_ABSTRACT_FUNCに を呼び出す関数を含む新しいソース ファイルを生成することができます。insert().

モックアップの完全なバージョンは、ここにあります。

于 2013-05-16T18:41:29.010 に答える