C语言 – 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类的方法实际上是声明函数的函数指针。在准备实例时StringnewString调用该函数以设置指向其各自函数的函数指针:

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

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

    self->set(self, "");

    return self;
}

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

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方法将无法访问,同时保持对get和的访问权限length,并强制“构造函数”接受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方法的声明不存在,因此,它不能在a中调用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;
}

在实例化时ImmutableString,指向getlength方法的函数指针实际上是通过遍历内部存储对象的变量来引用String.getString.length方法。baseString

使用函数指针可以实现从超类继承方法。

我们可以进一步继续C中的多态性

例如,如果我们想要改变length方法的行为以便出于某种原因0ImmutableString类中返回所有的时间,那么所有必须做的就是:

  1. 添加一个将用作覆盖length方法的函数。
  2. 转到“构造函数”并将函数指针设置为重写length方法。

添加覆盖length方法ImmutableString可以通过添加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;
}

现在,不是lengthImmutableString类中作为String类的方法具有相同的行为,现在该length方法将引用lengthOverrideMethod函数中定义的行为。

我必须添加一个免责声明,我仍然在学习如何使用C语言中的面向对象编程风格进行编写,因此可能有一点我没有解释得很好,或者可能只是在如何最好地实现OOP在C.但我的目的是试图说明函数指针的许多用法之一。

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

添加评论

友情链接:蝴蝶教程