温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2020-06-28 18:46:17 来源:网络 阅读:817 作者:shangluyi 栏目:编程语言

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


这篇文章的所有代码都是用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;
}



(完)

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI