C中的函数指针如何工作?

我最近在C中使用了函数指针。

因此,继续回答您自己的问题的传统,我决定对那些需要快速介入该主题的人做一个基本的小结。


C中的函数指针

我们先来看一个我们将要指出的基本功能:

int addInt(int n, int m) {
    return n+m;
}

首先,让我们定义一个指向一个函数的指针,它接收2个int并返回一个int

int (*functionPtr)(int,int);

现在我们可以安全地指向我们的功能:

functionPtr = &addInt;

现在我们有了一个指向函数的指针,让我们使用它:

int sum = (*functionPtr)(2, 3); // sum == 5

将指针传递给另一个函数基本上是相同的:

int add2to3(int (*functionPtr)(int, int)) {
    return (*functionPtr)(2, 3);
}

我们也可以在返回值中使用函数指针(尝试跟上,它变得凌乱):

// this is a function called functionFactory which receives parameter n
// and returns a pointer to another function which receives two ints
// and it returns another int
int (*functionFactory(int n))(int, int) {
    printf("Got parameter %d", n);
    int (*functionPtr)(int,int) = &addInt;
    return functionPtr;
}

但是使用typedef会更好:

typedef int (*myFuncDef)(int, int);
// note that the typedef name is indeed myFuncDef

myFuncDef functionFactory(int n) {
    printf("Got parameter %d", n);
    myFuncDef functionPtr = &addInt;
    return functionPtr;
}

C中的函数指针可以用来在C中执行面向对象的编程。

例如,以下行用C语言编写:

String s1 = newString();
s1->set(s1, "hello");

是的, ->和缺少一个new操作符是一个死的放弃,但它肯定似乎暗示我们将某些String类的文本设置为"hello"

通过使用函数指针, 可以模拟C中的方法

这是如何完成的?

String类实际上是一个带有大量函数指针的struct ,这些函数指针可以用来模拟方法。 以下是String类的部分声明:

typedef struct String_Struct* String;

struct String_Struct
{
    char* (*get)(const void* self);
    void (*set)(const void* self, char* value);
    int (*length)(const void* self);
};

char* getString(const void* self);
void setString(const void* self, char* value);
int lengthString(const void* self);

String newString();

可以看出, String类的方法实际上是指向声明函数的函数指针。 在准备String实例时,调用newString函数来设置函数指针,以指向它们各自的函数:

String newString()
{
    String self = (String)malloc(sizeof(struct String_Struct));

    self->get = &getString;
    self->set = &setString;
    self->length = &lengthString;

    self->set(self, "");

    return self;
}

例如,调用get方法调用的getString函数定义如下:

char* getString(const void* self_obj)
{
    return ((String)self_obj)->internal->value;
}

可以注意到的一件事是,没有对象实例的概念,并且实际上有方法是对象的一部分,因此必须在每次调用时传入“自我对象”。 (而internal只是一个隐藏的struct ,它之前从代码清单中省略 - 它是一种执行信息隐藏的方式,但与函数指针无关。)

所以,而不是能够做s1->set("hello"); ,必须传入对象才能对s1->set(s1, "hello")执行操作。

有了这个小小的解释,我们就不得不将自己的引用传递给自己,我们将转向下一部分,这是C中的继承

假设我们想要创建一个String的子类,比如一个ImmutableString 。 为了使字符串不可变, set方法将不可访问,同时保持对getlength访问,并强制“构造函数”接受char*

typedef struct ImmutableString_Struct* ImmutableString;

struct ImmutableString_Struct
{
    String base;

    char* (*get)(const void* self);
    int (*length)(const void* self);
};

ImmutableString newImmutableString(const char* value);

基本上,对于所有的子类,可用的方法都是函数指针。 这次, set方法的声明不存在,因此它不能在ImmutableString调用。

至于ImmutableString的实现,唯一相关的代码是“构造函数”函数newImmutableString

ImmutableString newImmutableString(const char* value)
{
    ImmutableString self = (ImmutableString)malloc(sizeof(struct ImmutableString_Struct));

    self->base = newString();

    self->get = self->base->get;
    self->length = self->base->length;

    self->base->set(self->base, (char*)value);

    return self;
}

在实例化ImmutableStringgetlength方法的函数指针通过遍历base变量(这是一个内部存储的String对象)实际引用String.getString.length方法。

函数指针的使用可以实现父类中方法的继承。

我们可以继续在C中继承多态

例如,如果我们想要改变length方法的行为,以便由于某种原因在ImmutableString类中始终返回0 ,所有必须完成的操作是:

  • 添加一个将用作覆盖length方法的函数。
  • 转到“构造函数”并将函数指针设置为覆盖length方法。
  • ImmutableString添加重写length方法可以通过添加lengthOverrideMethod来执行:

    int lengthOverrideMethod(const void* self)
    {
        return 0;
    }
    

    然后,构造函数中length方法的函数指针被连接到lengthOverrideMethod

    ImmutableString newImmutableString(const char* value)
    {
        ImmutableString self = (ImmutableString)malloc(sizeof(struct ImmutableString_Struct));
    
        self->base = newString();
    
        self->get = self->base->get;
        self->length = &lengthOverrideMethod;
    
        self->base->set(self->base, (char*)value);
    
        return self;
    }
    

    现在,而不是具有用于相同的行为length在方法ImmutableString类作为String类,现在length方法将是指在定义的行为lengthOverrideMethod功能。

    我必须添加一个免责声明,我仍然在学习如何用C编写一个面向对象的编程风格,所以可能有些问题我没有解释清楚,或者可能只是关于如何最好地实现面向对象编程在C中,但我的目的是试图说明函数指针的许多用途之一。

    有关如何在C中执行面向对象编程的更多信息,请参阅以下问题:

  • 面向对象的C?
  • 你能用C编写面向对象的代码吗?

  • 被引发的指南:如何通过手动编译代码来滥用x86机器上的GCC中的函数指针:

  • 返回EAX寄存器的当前值

    int eax = ((int(*)())("xc3 <- This returns the value of the EAX register"))();
    
  • 写一个交换功能

    int a = 10, b = 20;
    ((void(*)(int*,int*))"x8bx44x24x04x8bx5cx24x08x8bx00x8bx1bx31xc3x31xd8x31xc3x8bx4cx24x04x89x01x8bx4cx24x08x89x19xc3 <- This swaps the values of a and b")(&a,&b);
    
  • 写一个for循环计数器到1000,每次调用一些函数

    ((int(*)())"x66x31xc0x8bx5cx24x04x66x40x50xffxd3x58x66x3dxe8x03x75xf4xc3")(&function); // calls function with 1->1000
    
  • 你甚至可以编写一个计数为100的递归函数

    const char* lol = "x8bx5cx24x4x3dxe8x3x0x0x7ex2x31xc0x83xf8x64x7dx6x40x53xffxd3x5bxc3xc3 <- Recursively calls the function at address lol.";
    i = ((int(*)())(lol))(lol);
    
  • 链接地址: http://www.djcxy.com/p/43835.html

    上一篇: How do function pointers in C work?

    下一篇: Getting error on DNN site