函數指針和回調函數

函數指針

1.函數指針的定義:

void (*p)(void)   :沒有返回值,沒有參數的函數指針p

void (*p)(int,int)  :沒有返回值,參數爲兩個整型的函數指針p

int (*p)(void)     :返回值爲整型,沒有參數的函數指針p

char (*p)(int,int)  :返回值爲字符型,參數爲整型的函數指針p

..................................

2.函數指針的賦值:

void fun1()
{
   printf("i am is fun1\r\n");
}
int main(int argc,char*argv[])
{
  void (*p1)(void);
  p1 =fun1;
}

3.函數指針的執行:

int main(int argc,char*argv[])
{
  void (*p1)(void);
  p1 =fun1;
  p1();        //簡寫方式
  //(*p1)();    //原始方式
  while(1);
}

那麼問題了,我們有函數不去調用,爲什麼還要定義一個函數指針去執行對應的函數,再去用函數指針來執行,看上去多此一舉,是吧?是的,這裏就是多此一舉,不過有一個好處就是定義的函數指針可以隨意指向與其類型一致的任意函數,這裏只是指向fun1,同樣也可以指向fun2。

回調函數

下面我們用兩個.c文件和一個run.h文件來實現另一個功能,定義main.c和run.c文件,在run.c和run.h的內容如下:

#include<Windows.h>
//定義函數指針
void (*p)(void);
//運行函數指針
void run()
{
  while(1)
  {
     (*p)();
	 Sleep(1000);
  } 
}

#ifndef RUN_H_
#define RUN_H_
void run(void);
#endif

run.c中定義了一個函數指針,且在run函數中直接對函數指針p運行,此方式存在着一定的bug,使用者在main.c中調用了run函數,卻沒對函數指針賦值,那不是會出現段錯誤了嗎?我們先來嘗試一下,main函數代碼如下:

#include<stdio.h>
#include "run.h"

//聲明函數指針p,不然不能調用
extern void (*p)(void);
void fun1()
{
   printf("i am is fun1\r\n");
}
void fun2()
{
   printf("i am is fun2\r\n");
}
int main(int argc,char*argv[])
{
  run();      
}

果不其然,出現了段錯誤。

爲了避免悲劇的誕生,需要對run.c中的run函數進行優化,定義函數指針p的時候對其初始化 void (*p)(void) = NULL 操作,在執行函數指針p之前判斷p是否賦值這樣就不會出現錯誤了,if(p!= NULL)   (*p)();

main.c中添加對函數指針的賦值,運行程序,run.c中的run函數會每秒調用一次main.c中的fun1函數。

一般在run.c中添加一個安裝函數 install_fun,該函數帶有一個函數指針參數,直接將函數名傳進來,其中的 p是我們之前在run.c中定義的函數指針,形式是 void (*p)(void),具體如下:

//添加安裝函數
void install_fun(void (*pp)(void))
{
	p = pp;
}

這樣在main.c可以刪除對函數指針p的聲明,直接聲明install_fun函數,具體代碼如下:

#include<stdio.h>
#include "run.h"

void install_fun(void (*p)(void));
void fun1()
{
   printf("i am is fun1\r\n");
}
int main(int argc,char*argv[])
{
  install_fun(fun1) ;   
  run();      
}

運行結果跟上面一樣,但這種看上去更加牛X,瞬間提高了逼格,以後我們應該學着去寫一些有逼格的東西,顯得自己不是一個菜鳥哈........

你以爲結束了嗎?遠遠沒有,有時候我們想從底層拿些數據,是不是可以通過回調函數來拿取呢?肯定可以的,修改上面的代嗎可以做到,首先修改run.c文件中的函數指針增加參數int 形式如: void (*p)(int) 在run函數中,添加一個計數變量,循環自加,並將此變量傳給應用層的回調函數中。

#include<Windows.h>
//定義函數指針
void (*p)(int) = NULL;
//運行函數指針
void run()
{
 int count = 0;
  while(1)
  {
	if(p!= NULL)
     (*p)(count);
	 count++;
	 Sleep(1000);
  } 
}
//添加安裝函數
void install_fun(void (*pp)(int))
{
	p = pp;
}

在main.c中爲fun1添加一個參數 int i 來接收底層傳入的參數count,這樣就可以通過回調函數的形式來接收底層傳入的參數了。

#include<stdio.h>
#include "run.h"
void install_fun(void (*p)(int));
void fun1(int i)
{
   printf("i am is fun1 time is %d s\r\n",i);
}
int main(int argc,char*argv[])
{
  install_fun(fun1) ;   
  run();      
}

運行結果如下,成功打印出run.c文件run函數中cout變量傳上來的值。

到這裏,我們可以把單片機採集的傳感器數據都可以使用回調函數的新式來傳入到main.c中,前提是你得把每一個的底層封裝好,甚至做到客戶不可見,我只管給你數據,你只管來調用就行。

這裏同樣有一個疑問,既然底層可以傳回數據到應用層,那麼應用層可以傳數據到底層嗎?當然可以的,這裏修改函數指針形式爲 int (*p) (int)  main.c和run.c的代嗎如下,可以看到應用層傳給底層一個2020的數據。

run.c 文件代碼
#include<Windows.h>
#include <stdio.h>
//定義函數指針
int (*p)(int) = NULL;
//運行函數指針
void run()
{
 int count = 0;
  while(1)
  {
	if(p!= NULL)
	{
	 int r = (*p)(count);
	 printf("%d \r\n",r);
	}
	 count++;
	 Sleep(1000);
  } 
}
//添加安裝函數
void install_fun(int (*pp)(int))
{
	p = pp;
}

Main.c 文件代碼
#include<stdio.h>
#include "run.h"
void install_fun(int (*p)(int));
int fun1(int i)
{
   printf("i am is fun1 time is %d s\r\n",i);
   return 2020;
}
int main(int argc,char*argv[])
{
  install_fun(fun1) ;   
  run();      
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章