一篇文章引发我对函数指针的思考

为什么要写这篇

  本来以为我对函数指针掌握的可以了,可以用于回调函数,可以用来做跳转表。直到前几天看到一篇文章嵌入式中的合作开发–函数指针 刷新了我的认知,知道自己认识得还不够,这么实用的一个功能竟然没有发掘到。

  在嵌入式软件开发中,一个项目往往需要多人协作完成。现在完成程序移植工作时深有体会,这时候想到的是公司有个规范就好了,都按照这个规范来就可以减少不少费时费力的工作,提高些工作效率。前些天我记得看见嵌入式大杂烩分享了一篇,该拜读拜读,但是小公司形不成规范也不是啥难理解的事,因为工程师之间的协作并不频繁。公司没有要求,但自己有体会,有想让自己脱离这种现状的迫切。

  A完成项目的整体逻辑功能,A不能面面俱到,因此一些函数的具体实现交给了B来完成,B要完成的函数很多,A就不大可能一个个名字想好之后再交给B,这就导致B提高的函数名的命名规则不符合A的习惯,A用起来很不爽。A可以自己再声明一个自己喜欢的函数名,并通过函数指针来获得B的功能。

  前些天有个同事要针对一款蓝牙芯片写一个公版程序,他把底层接口函数和QC2.0协议 源文件交给我来实现,实现后函数的命名方式和他的公版程序有点儿格格不入,于是应他的要求有把命名都改成了他要求的那种,而我依据原来命名方式调用的文件呀,统统都替换掉了,那叫个费事呀。

  了解到通过函数指针可以实现这种方式后,就想着怎么用上。

定义函数的文件

#include "definefunc.h"

/**
 * set_gpio_init_input
 * @brief   set gpio init to input
 * @param   gpio_pin 参数描述: gpio pin
 */
static void
set_gpio_init_input(Gpio_Pin gpio_pin)
{
    
    
    printf("from function : %s \n", __FUNCTION__);
}

/**
 * set_gpio_init_output
 * @brief   set gvset gpio init to output
 * @param   gpio_pin 参数描述: gpio pin
 */
static void
set_gpio_init_output(Gpio_Pin gpio_pin)
{
    
    
    printf("from function : %s \n", __FUNCTION__);
}

/**
 * AQ_Gpio_Mode_Set
 * @brief   set gvset gpio init to input/output
 * @param   gpio_pin 参数描述: gpio pin 
 * @param   direction 参数描述: input / output
 */
void 
AQ_Gpio_Mode_Set(Gpio_Pin gpio_pin, Gpio_Direction direction)
{
    
    
    if(direction == GPIO_INPUT)
    {
    
    
        set_gpio_init_input(gpio_pin);
    }else if(direction == GPIO_OUTPUT)
    {
    
    
        set_gpio_init_output(gpio_pin);
    }else
    {
    
    
        /* no code */
    }
}

/**
 * AQ_Get_Gpio_Input_Data
 * @brief   get gpio input level
 * @param   gpio_pin 参数描述: gpio pi
 * @retval  Gpio_Level 返回值描述: high / low
 */
Gpio_Level 
AQ_Get_Gpio_Input_Data(Gpio_Pin gpio_pin)
{
    
    
    printf("from function : %s \n", __FUNCTION__);
}

/**
 * AQ_Gpio_Output_Data
 * @brief   set gpio output level
 * @param   gpio_pin 参数描述: gpio pin
 * @param   gpio_data 参数描述: high / low
 */
void 
AQ_Gpio_Output_Data(Gpio_Pin gpio_pin, Gpio_Level gpio_data)
{
    
    
    printf("from function : %s \n", __FUNCTION__);
}

/**
 * AQ_Gpio_Output_Data_toggle
 * @brief   set gpio output level toggle
 * @param   gpio_pin 参数描述: gpio pin
 */
void
AQ_Gpio_Output_Data_toggle(Gpio_Pin gpio_pin)
{
    
    
	printf("from function : %s \n", __FUNCTION__);
}

/**
 * AQ_Get_Adc_Data_mV
 * @brief   get adc filter mV value
 * @param   channel 参数描述: adc channel
 * @retval  Gpio_Data 返回值描述: gpio data ,Range : 0 ~ 5500mV
 */
Gpio_Data 
AQ_Get_Adc_Data_mV(Adc_Channel channel)
{
    
    
    printf("from function : %s \n", __FUNCTION__);
}

定义函数的头文件

#ifndef __DEFINEFUNC_H__
#define __DEFINEFUNC_H__

#include <stdio.h>
#include <stdint.h>

typedef uint8_t Gpio_Pin;
typedef uint32_t  Adc_Channel;

typedef uint32_t Gpio_Data;

typedef enum direction
{
    
    
    GPIO_INPUT = 1,
    GPIO_OUTPUT,
} Gpio_Direction;

typedef enum level
{
    
    
    GPIO_DATA_LOW_LEVEL = 0,
    GPIO_DATA_HIGH_LEVEL = 1,
}Gpio_Level;

void AQ_Gpio_Mode_Set(Gpio_Pin gpio_pin, Gpio_Direction direction);
Gpio_Level AQ_Get_Gpio_Input_Data(Gpio_Pin gpio_pin);
void AQ_Gpio_Output_Data(Gpio_Pin gpio_pin, Gpio_Level gpio_data);
void AQ_Gpio_Output_Data_toggle(Gpio_Pin gpio_pin);
Gpio_Data AQ_Get_Adc_Data_mV(Adc_Channel channel);

#endif

重定义函数名的源文件

#include "redefinefunc.h"


Gpio_Level (*get_gpio_input_data)(Gpio_Pin gpio_pin) = NULL;
void (*set_gpio_output_data)(Gpio_Pin gpio_pin, Gpio_Level gpio_data) = NULL;
void (*set_gpio_output_toggle)(Gpio_Pin gpio_pin) = NULL;
Gpio_Data (*get_adc_filter_mV_val)(Adc_Channel channel) = NULL;

int init_func_get_gpio_input_data(Gpio_Data(*func_handle)(Gpio_Pin));
int init_func_set_gpio_output_data(void(*func_handle)(Gpio_Pin, Gpio_Data));
int init_func_set_gpio_output_toggle(void(*func_handle)(Gpio_Pin));
int init_func_get_adc_filter_mV_val(Gpio_Data(*func_handle)(Adc_Channel));
/**
 * set_gpio_init_input
 * @brief   set gpio init to input
 * @param   gpio_pin 参数描述: gpio pin
 */
void 
set_gpio_init_input(Gpio_Pin gpio_pin)
{
    
    
	AQ_Gpio_Mode_Set(gpio_pin, GPIO_INPUT);
}

/**
 * set_gpio_init_output
 * @brief   set gvset gpio init to output
 * @param   gpio_pin 参数描述: gpio pin
 */
void 
set_gpio_init_output(Gpio_Pin gpio_pin)
{
    
    
	AQ_Gpio_Mode_Set(gpio_pin, GPIO_OUTPUT);
}

/**
 * init_func_get_gpio_input_data
 * @brief   initialize the function : get_gpio_input_data
 * @param   func_handle 参数描述: function pointer
 * @retval  int 返回值描述: OK
 */
int init_func_get_gpio_input_data(Gpio_Data(*func_handle)(Gpio_Pin))
{
    
    
	get_gpio_input_data = func_handle;

	return 1;
}

/**
 * init_func_set_gpio_output_data
 * @brief   initialize the function : set_gpio_output_data
 * @param   func_handle 参数描述: function pointer
 * @retval  int 返回值描述: OK
 */
int init_func_set_gpio_output_data(void(*func_handle)(Gpio_Pin, Gpio_Data))
{
    
    
	set_gpio_output_data = func_handle;

	return 1;
}

/**
 * init_func_set_gpio_output_toggle
 * @brief   initialize the function : set_gpio_output_toggle
 * @param   func_handle 参数描述: function pointer
 * @retval  int 返回值描述: OK
 */
int init_func_set_gpio_output_toggle(void(*func_handle)(Gpio_Pin))
{
    
    
	set_gpio_output_toggle = func_handle;

	return 1;
}

/**
 * init_func_get_adc_filter_mV_val
 * @brief   initialize the function : get_adc_filter_mV_val
 * @param   func_handle 参数描述: function pointer
 * @retval  int 返回值描述: OK
 */
int init_func_get_adc_filter_mV_val(Gpio_Data(*func_handle)(Adc_Channel))
{
    
    
	get_adc_filter_mV_val = func_handle;

	return 1;
}

/**
 * init_interface_function
 * @brief   initialize the interface function
 */
void init_interface_function(void)
{
    
    
	init_func_get_gpio_input_data(AQ_Get_Gpio_Input_Data);
	init_func_set_gpio_output_data(AQ_Gpio_Output_Data);
	init_func_set_gpio_output_toggle(AQ_Gpio_Output_Data_toggle);
	init_func_get_adc_filter_mV_val(AQ_Get_Adc_Data_mV);

}

重定义函数名的头文件

#ifndef __REDEFINEFUNC_H__
#define __REDEFINEFUNC_H__

#include "definefunc.h"

void set_gpio_init_input(Gpio_Pin gpio_pin);
void set_gpio_init_output(Gpio_Pin gpio_pin);

void init_interface_function(void);

extern Gpio_Level (*get_gpio_input_data)(Gpio_Pin gpio_pin);
extern void (*set_gpio_output_data)(Gpio_Pin gpio_pin, Gpio_Level gpio_data);
extern void (*set_gpio_output_toggle)(Gpio_Pin gpio_pin);
extern Gpio_Data (*get_adc_filter_mV_val)(Adc_Channel channel);

#endif

测试函数调用

#include "redefinefunc.h"


int main(void)
{
    
    

    init_interface_function();
    
    set_gpio_init_input(1);
    set_gpio_init_output(1);

    get_gpio_input_data(1);
    set_gpio_output_data(1, 1);
    set_gpio_output_toggle(1);
    get_adc_filter_mV_val(1);

    return 0;
}

运行结果

image-20210315165933869

代码优化

  现在这样虽然达到了目的,但改动起来还是太大了,虽然没有函数重写,但也只比重写省事了一丢丢。总的来说就是重新定义相关函数类型的函数指针变量,然后通过函数给其赋值,然后将函数指针变量声明为外部调用,若函数赋值在函数调用之后,那么直接导致程序跑飞,要时常注意这问题那真谈不上多好用。
  更好的方法是函数指针变量直接在定义时赋值, 而不是NULL,然后直接声明为外部调用。

.c文件
Gpio_Level (*get_gpio_input_data)(Gpio_Pin gpio_pin) = AQ_Get_Gpio_Input_Data;

.h文件
extern Gpio_Level (*get_gpio_input_data)(Gpio_Pin gpio_pin);

  你认为这已经是最好的方法了吗?实际上还可以化简。将声明,定义和初始化 合而为一,去掉源文件,直接写在头文件里。因为在头文件里定义变量会导致被引用时,同一变量被多次分配内存,因此要将其前面加static声明为静态的。

.h文件
static Gpio_Level (*get_gpio_input_data)(Gpio_Pin gpio_pin) = AQ_Get_Gpio_Input_Data;

猜你喜欢

转载自blog.csdn.net/quanquanxiaobu/article/details/114845374