函數指針


前言:

先看兩個基礎,函數指針和extern關鍵字,然后由一個具體的例子,具體使用下函數指針。

一、基礎

函數指針:即指向函數的指針,本質還是一個指針。
函數指針的聲明:返回值類型 ( * 指針變量名) ([形參列表]);
注意這里是聲明不是定義,聲明之后它就是一個類型了(與int,char,int *等級別等同,這點有點像結構體),然后就可以定義、使用了。
舉例如下(下面這段小程序摘自百度百科):

int max(int x,int y){return (x>y? x:y);}
int main()
{
    int (*ptr)(int, int);
    int a, b, c;
    ptr = max;
    scanf("%d%d", &a, &b);
    c = (*ptr)(a,b);
    printf("a=%d, b=%d, max=%d", a, b, c);
    return 0;
}

 

extern:extern可以置於變量或者函數前,以標示變量或者函數的定義在別的文件中,提示編譯器遇到此變量和函數時在其他模塊中尋找其定義。此外extern也可用來進行鏈接指定。也就是說extern有兩個作用,第一個,當它與"C"一起連用時,如: extern "C" void fun(int a, int b);則告訴編譯器在編譯fun這個函數名時按着C的規則去翻譯相應的函數名而不是C++的,C++的規則在翻譯這個函數名時會把fun這個名字變得面目全非,可能是fun@aBc_int_int#%$也可能是別的,因為C++支持函數的重載。
第二,當extern不與"C"在一起修飾變量或函數時,如在頭文件中: extern int g_Int; 它的作用就是聲明函數或全局變量的作用范圍的關鍵字,其聲明的函數和變量可以在本模塊活其他模塊中使用,記住它是一個聲明不是定義!也就是說B模塊(編譯單元)要是引用模塊(編譯單元)A中定義的全局變量或函數時,它只要包含A模塊的頭文件即可,在編譯階段,模塊B雖然找不到該函數或變量,但它不會報錯,它會在連接時從模塊A生成的目標代碼中找到此函數。

二、舉例

這里說一下背景,假如我這里做一個平台,我一套代碼要交付到多個產品,然后結合產品代碼進行使用,那么我做平台肯定不能為每個產品做一套代碼,但是有時候同樣一個功能,各個產品之間會出現差異,這個時候指針函數就派上用場了,我給各個產品提供一個指針函數定義的變量,然后各個產品將自己的實現函數掛接在上面,這樣就屏蔽了各個產品的差異,甚至有些產品可以不掛接,那么我就判斷一下,如果沒掛接,我就給一個默認的實現就ok了(這部分在下面代碼中沒體現).

代碼如下:

平台代碼:

#include<iostream>
#include"lib_main.h"
using namespace std;

funcs g_hook_func;

void hook_func_init()
{
    g_hook_func.func1=NULL;
    g_hook_func.func2=NULL;
}

int main()
{
    char name[10];
    int result = 0 ;

    memset(name,0,sizeof(name));
        
    hook_func_init();

    hook_func();//鈎子掛接函數,多線程情況應該在產品側掛接

    if(g_hook_func.func1 != NULL)
    {
        if(0 == g_hook_func.func1(name))
        {
            cout<<"err";
            return -1;
        }
    }
    if(g_hook_func.func2 != NULL)
    {
        result = g_hook_func.func2(1,2);
    }

    cout<<name<<"  "<<result<<endl;
    return 0;
}

 

 

平台頭文件:

#ifndef _LIB_MAIN_H_
#define _LIB_MAIN_H_

typedef struct func
{
    int (*func1)(char * str);
    int (*func2)(int a,int b);
}funcs;
extern void hook_func();
#endif

 

產品1代碼:

#include"wlan.h"
#include<iostream>
using namespace std;

static int getname(char * str)
{
    if(NULL == str)
    {
        return 0;
    }
    //入參大小由調用者保證不越界
    str[0]='w';
    str[1]='l';
    str[2]='a';
    str[3]='n';
    str[4]='\0';
    return 1;
}

static int add(int a, int b)
{
    return (a+b+3);
}

void hook_func()
{
    g_hook_func.func1 = getname;
    g_hook_func.func2 = add;
}

產品1頭文件代碼:

#ifndef _WLAN_H_
#define _WLAN_H_


typedef struct func
{
    int (*func1)(char * str);
    int (*func2)(int a,int b);
}funcs;

extern funcs g_hook_func;

#endif

產品2代碼:

#include"ar.h"
#include<iostream>
using namespace std;

static int getname(char * str)
{
    if(NULL == str)
    {
        return 0;
    }
    //入參大小由調用者保證不越界
    str[0]='a';
    str[1]='r';
    str[2]='\0';
    return 1;
}

static int add(int a, int b)
{
    return (a+b+1);
}

void hook_func()
{
    g_hook_func.func1 = getname;
    g_hook_func.func2 = add;
}

產品2頭文件:

#ifndef _AR_H_
#define _AR_H_


typedef struct func
{
    int (*func1)(char * str);
    int (*func2)(int a,int b);
}funcs;

extern funcs g_hook_func;


#endif

產品3代碼:

#include"fw.h"
#include<iostream>
using namespace std;

static int getname(char * str)
{
    if(NULL == str)
    {
        return 0;
    }
    //入參大小由調用者保證不越界
    str[0]='f';
    str[1]='w';
    str[2]='\0';
    return 1;
}

static int add(int a, int b)
{
    return (a+b+2);
}

void hook_func()
{
    g_hook_func.func1 = getname;
    g_hook_func.func2 = add;
}

產品3頭文件:

#ifndef _FW_H_
#define _FW_H_


typedef struct func
{
    int (*func1)(char * str);
    int (*func2)(int a,int b);
}funcs;

extern funcs g_hook_func;

#endif

說明:

1、上面的所有代碼不能同時運行,想一下也應該知道,應該是一套lib和一套產品放在一個工程下運行。

2、多線程條件下掛接鈎子的函數hook_func應該在產品側掛接,這樣即使沒有掛接,在lib側也沒有影響。

3、平台和產品側的結構都要進行聲明,且要一致typedef struct func { int (*func1)(char * str); int (*func2)(int a,int b); }funcs;

    注意這里是聲明,不是定義,所以不會分配內存,聲明只是表示我這里現在有了這種類型(就像是說我這里有一個int一樣)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM