回顾向:函数指针&回调函数&面向对象风格的C语言

面试的时候发现这个地方掌握的不够好,所以单独复习下,顺便写一篇博客。

10年积累的成都网站设计、网站制作经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站设计后付款的网站建设流程,更有溧水免费网站建设让你可以放心的选择与我们合作。

这篇文章的所有代码都是用C实现的,这是由于C没有面向对象的特性,所以我希望用函数指针把C的结构体弄得有一点面向对象的感觉。

先介绍函数指针、回调函数,然后用C结合回调函数实现一个有面向对象风格的链表

函数指针:

函数指针,就是指向函数的指针

基本的函数指针(无参数无返回值)

//函数指针
void fun(  )
{
	printf("fun()\n");
}

int main()
{
	//void *fp1() = &fun
	void (*fp1)();
	fp1 = fun;
	fp1();
	return 0;
}

有参数

//带参数
void fun(int val1, int val2)
{
	printf("val1 = %d, val2 = %d\n", val1, val2);
}

int main()
{
	void(*fp)( int val1, int val2 ) = &fun;
	fp(1, 2);
	return 0;
}

有参数及返回值

//带参数及返回值
int fun(int val1, int val2)
{
	return val1 + val2;
}

int main()
{
	int(*fp)(int val1, int val2) = &fun;
	int ret = fp(1, 2);
	printf("%d\n", ret);
	return 0;
}

但是,这样定义函数指针非常影响可读性,特别是对于复杂的函数,比如参数或者返回值也是函数指针类型的情况,因此,通常可以用typedef 如下:

//typedef
typedef int (*FP)(int val1, int val2);

int fun(int val1, int val2)
{
	return val1 + val2;
}

int main()
{
	FP fp1 = &fun;
	int ret = fp1(1, 2);
	printf("%d\n", ret);
	return 0;
}

回调函数:

而回调函数的实现,则利用了函数指针

简单的说,回调函数是一个函数,它的参数是函数指针和该指针所指向函数的参数,作为原本执行函数和目标函数之间的中介

回顾向 : 函数指针 & 回调函数  & 面向对象风格的C语言

为什么要用回调函数呢?回调函数的优势在于它的灵活性

通常我们调用函数,是被编译器最终转化成汇编语言、二进制文件的,板上钉钉的事情,这是事先决定好的,是静态的

但回调函数就不一样了,回调函数接受的参数,只是一个未知的函数的地址,是只有在执行时才可知的,是动态的

验证的标准就是,对于回调函数,如果传入的参数不是函数的地址,而是其他某个变量的地址,编译仍能正确执行,只有在运行时才出错。

上述的“动态特性” 是不是很像 C++中发生继承时的动态绑定呢?

简单的应用:

这里不得不顺便说一下,C++的相同自定义类的不通对象中的成员方法是公用的,怎么测试呢,看下面的C++代码

class Test
{
public:
    int _val = 20;
    void _Print()
    {
        std::cout << "hello?"" << endl;
    }
};

int main()
{
    Test *pt = NULL;
    t1._Print();//能执行
    t1._val = 0; //崩溃,因为t1并没有指向一个Test类型的实例,因此当然访问不到_val成员了
}

执行上述的代码,会发现即使整个代码并没有 Test对象的实例化,但仍能正确地调用Test中的_Print方法,这时由于C++类中的成员方法是公用的,并且并不是存放在每个类的对象中的,这么做当然是为了节省空间,那么我们能不能稍微借助一下这样的设计思路,用C简单的实现一个C++的类呢?

下面我将给结构体struct中定义函数指针,看看效果:

void fun()
{
	printf("hello\n");
}

typedef struct ST
{
	void(*_pf)();
}ST;

void Set( ST **st ) //类似构造函数
{
	*st = (ST*)malloc(sizeof(ST));
	(*st)->_pf = &fun;
}

int main()
{
	ST *s1 = NULL;
	Set(&s1);     //初始化,这里貌似没办法在内部访问了
	s1->_pf();    //像不像C++呢
	return 0;
}

在这段代码中,类ST是一个函数指针,而main函数的前两行则可以认为是C++中的构造函数,它的作用是将结构指针进行赋值,我定义这个函数是由于C中的结构体中的变量不支持在结构体内初始化。

然后我就可以像C++调用成员函数的方法了。

当然,这并不是原汁原味的C++的实现方式,C++是通过静态绑定的方式,在编译时就确定函数的位置的,而我是用动态的方式实现的,我的结构体中需要声明若干函数指针。

OK,接下来再实现一个简单的链表

struct LinkListNode;
typedef void(*pPushBack)(struct LinkListNode *This, int data);
struct LinkListNode* BuyNode(data);

typedef struct LinkListNode
{
	int _val;
	struct LinkListNode *_next;
	pPushBack _PushBack;
}LinkListNode;

void PushBack(LinkListNode *This, int data)
{
	if (This == NULL)
	{
		This = BuyNode(data);
	}
	LinkListNode *NewNode = BuyNode(data);
	LinkListNode *cur = This;
	while (cur && cur->_next != NULL)
	{
		cur = cur->_next;
	}
	cur->_next = NewNode;
}

LinkListNode* BuyNode(int data)
{
	LinkListNode *NewNode = (LinkListNode*)malloc(sizeof(LinkListNode));
	NewNode->_val = data;
	NewNode->_next = NULL;
	NewNode->_PushBack = &PushBack;
	return NewNode;
}

void Init(struct LinkListNode **This, int data)
{
	(*This) = BuyNode(data);
	(*This)->_val = data;
	(*This)->_next = NULL;
	(*This)->_PushBack = &PushBack;
}

int main()
{
	LinkListNode *Node1 = NULL;
	Init(&Node1, 1);
	Node1->_PushBack(Node1, 2);
	Node1->_PushBack(Node1, 3);
	Node1->_PushBack(Node1, 4);
	return 0;
}

(完)


网站栏目:回顾向:函数指针&回调函数&面向对象风格的C语言
文章路径:http://hbruida.cn/article/gpjodd.html