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

27 浏览
0 Comments

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

我最近在C语言中使用了一些函数指针的经验。

因此,继续回答自己的问题的传统,我决定为那些需要快速入门该主题的人制作一个小总结。

admin 更改状态以发布 2023年5月21日
0
0 Comments

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;
}

其中一个可以注意到的事情是,不存在对象的实例以及实际上是对象一部分的方法的概念,因此必须在每次调用时传入一个“self object”(自身对象)。 (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;
}

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

通过函数指针的使用,可以实现从超类继承方法的功能。

我们可以继续了解C中的多态

例如,如果我们想要由于某种原因在ImmutableString类中始终返回 0的长度,我们只需要:

  1. 添加一个将作为覆盖length方法的功能。
  2. 进入“构造函数”,将函数指针设置为覆盖length方法。

通过添加lengthOverrideMethod,可以在ImmutableString中添加一个覆盖length方法:

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;
}

现在,与String类中length方法的完全相同的行为不同,在ImmutableString类中的length方法将参考在lengthOverrideMethod函数中定义的行为。

我必须声明我仍然在学习如何用面向对象的编程风格在C中编写,所以可能有些点我没有解释好,或者可能在如何最好地实现C中的OOP方面有些离谱。但我的目的是试图说明函数指针的许多用途之一。

有关如何在C中进行面向对象程序设计的更多信息,请参见以下问题:

0
0 Comments

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;
}

0