Skip to content

LV030-函数指针的应用

前边已经学习了函数指针的概念和基本的应用,本篇笔记主要是函数指针的应用,在这里需要完成一个命令执行的过程。

一、命令处理函数

1. 函数定义

接收到命令的时候,需要使用函数指针指向对应的命令处理函数,我们的命令处理函数如下:

c
unsigned int func_name(int arg, int *pParam, void *pBuf)
{
    // ... ...
}

参数说明

  • arg :int类型参数,调用命令处理函数时传入。
  • pParam :int类型指针变量,调用命令处理函数时传入。
  • pBuf :void 类型指针变量,指向一块内存区域,用于接收命令下达时命令中的信息,可以自定义。

2. 函数声明

上边的函数定义之后肯定是需要声明的,如下所示:

c
unsigned int func_name(int arg, int *pParam, void *pBuf);

会发现,这样是有些长,当命令多起来的时候,他们唯一的区别就是函数名不同,这样我们可以使用一个宏定义,定义一下函数的声明格式:

c
#define HOST_CMD_PROCESS_FUNC(_name_) unsigned int _name_(int arg, int *pParam, void *pBuf) // 方便定义命令处理函数的函数名

注意】注意这里不要写成下边这样:

c
#define HOST_CMD_PROCESS_FUNC(_name_) (unsigned int _name_(int arg, int *pParam, void *pBuf))

这样可能会报以下问题:

shell
error: expected identifier or( before ‘unsigned’
 #define HOST_CMD_PROCESS_FUNC(_name_) (unsigned int _name_(int arg, int *pParam, void *pBuf))

声明实例

c
HOST_CMD_PROCESS_FUNC(HostTest1);

这就等价于:

c
unsigned int HostTest1(int arg, int *pParam, void *pBuf);

3. 函数指针定义

当我们需要调用命令处理函数的时候,就需要用到函数指针,我们可以定义如下:

c
typedef unsigned int (*Host_Cmd_Process_Func)(int arg, int *pParam, void *pBuf);            // 定义指向主机命令处理函数的指针

(1)(*Host_Cmd_Process_Func)中,Host_Cmd_Process_Func是一个指针变量;

(2)再看后边的括号,里边有三个参数,所以Host_Cmd_Process_Func可以指向一个带有三个参数的函数;

(3)最后看前边的unsigned int,说明函数返回值为 unsigned int 类型;

总的来说,Host_Cmd_Process_Func 可以指向一个返回值为 unsigned int 类型,含有三个参数的函数。

使用实例

c
Host_Cmd_Process_Func pFunc = NULL;

pFunc = (Host_Cmd_Process_Func)HostTest1;

pFunc(arg, (int *)&pParam, (void *)&cmdBuf);

二、命令信息

1. 命令描述

每一条命令都包含以下信息:

c
// 命令描述结构体——存放在命令数组中的每条命令要包含的信息数据
typedef struct
{
	unsigned long long value1;      // 8字节 64位
	unsigned long long value2;      // 8字节 64位
	Host_Cmd_Process_Func pFunc;    // 主机命令对应的处理函数
} HOST_CMD_DETAIL;

2. 命令信息组合

我们定义一个宏,来组合在写命令的时候想给命令的参数:

c
// 命令数组的成员组合——将数据组合,方便存放在命令数组中
#define MAKE_HOST_CMD(_wait_, _type_, _bPara_, _bRetPara_, _buf_, _retBuf_, _func_)   \
	{ \
		(_wait_ & 0xffff) | ((_type_ & 3) << 16) | ((_bPara_ & 1) << 18) | ((_bRetPara_ & 1) << 19), \
		((_retBuf_ & 0xffff) << 16) | (_buf_ & 0xffff), \
		_func_ \
	}

3. 命令数组定义

我们收到命令后,回到这些数组中寻找对应的命令信息,命令的处理函数也将在这里获取,这里:

c
// 保存每一条命令信息的数组
HOST_CMD_DETAIL hostCmdDetail[] =
{
	// 0,100
	MAKE_HOST_CMD(10000, 0,	1, 0, 0, 0, (Host_Cmd_Process_Func)HostTest1),
	// 1,101
	MAKE_HOST_CMD(10000, 0,	1, 0, 0, 0, (Host_Cmd_Process_Func)HostTest2),
};

// 保存每一条命令信息的数组
HOST_CMD_DETAIL smartHostCmdDetail[] =
{
	// 0,200
	MAKE_HOST_CMD(10000, 0,	1, 0, 0, 0, (Host_Cmd_Process_Func)SmartHostTest1),
	// 1,201
	MAKE_HOST_CMD(10000, 0,	1, 0, 0, 0, (Host_Cmd_Process_Func)SmartHostTest2),
};

4. 命令编号定义

c
// 命令的编号——每个命令的编号
typedef enum
{
	HOST_CMD_TEST1 = HOST_CMD_MIN, // 100
	HOST_CMD_TEST2,                // 101
	HOST_CMD_TEST3,                // 102
	// ... ...
	HOST_CMD_MAX,                  // 主机命令最大编号,在最大编号上边新增命令编号
	
	SMART_HOST_CMD_TEST1 = SMART_HOST_CMD_MIN, // 200
	SMART_HOST_CMD_TEST2,         // 201
	SMART_HOST_CMD_TEST3,         // 202
	// ... ...
	SMART_HOST_CMD_MAX,           // SMART主机命令最大编号,在最大编号上边新增命令编号
}HOST_CMD;

5. 命令区分

我们会有不同的命令,我们通过不同的值区间:

c
#define HOST_CMD_MIN         100  // 主机命令最小的命令编号,只有大于等于此值的命令编号才会被识别为主机命令
#define SMART_HOST_CMD_MIN   200  // SMART主机命令最小的命令编号,只有大于等于此值的命令编号才会被识别为SMART主机命令

三、实例

1. 源码实现

1.1 sys_cmd_info.h

c
#ifndef __SYS_CMD_INFO_H__
#define __SYS_CMD_INFO_H__

#define HOST_CMD_MIN         100  // 主机命令最小的命令编号,只有大于等于此值的命令编号才会被识别为主机命令
#define SMART_HOST_CMD_MIN   200  // SMART主机命令最小的命令编号,只有大于等于此值的命令编号才会被识别为SMART主机命令

#define GET_CMD_FUNC(_cmd_) ((_cmd_).pFunc) // 获取数组中元素的命令处理函数成员
#define HOST_CMD_PROCESS_FUNC(_name_) (unsigned int _name_(int arg, int *pParam, void *pBuf)) // 方便定义命令处理函数的函数名
typedef unsigned int (*Host_Cmd_Process_Func)(int arg, int *pParam, void *pBuf);            // 定义指向主机命令处理函数的指针

// 命令数组的成员组合——将数据组合,方便存放在命令数组中
#define MAKE_HOST_CMD(_wait_, _type_, _bPara_, _bRetPara_, _buf_, _retBuf_, _func_)   \
	{ \
		(_wait_ & 0xffff) | ((_type_ & 3) << 16) | ((_bPara_ & 1) << 18) | ((_bRetPara_ & 1) << 19), \
		((_retBuf_ & 0xffff) << 16) | (_buf_ & 0xffff), \
		_func_ \
	}

// 命令描述结构体——存放在命令数组中的每条命令要包含的信息数据
typedef struct
{
	unsigned long long value1;      // 8字节 64位
	unsigned long long value2;      // 8字节 64位
	Host_Cmd_Process_Func pFunc;    // 主机命令对应的处理函数
} HOST_CMD_DETAIL;

// 命令下达时携带的数据
typedef struct
{
	int a;
	char b;
} _CMD_DATA;

#endif

1.2 sys_cmd.h

c
#ifndef __SYS_CMD_H__
#define __SYS_CMD_H__

#include "sys_cmd_info.h"

// 命令的编号——每个命令的编号
typedef enum
{
	HOST_CMD_TEST1 = HOST_CMD_MIN, // 100
	HOST_CMD_TEST2,                // 101
	HOST_CMD_TEST3,                // 102
	// ... ...
	HOST_CMD_MAX,                  // 主机命令最大编号,在最大编号上边新增命令编号
	
	SMART_HOST_CMD_TEST1 = SMART_HOST_CMD_MIN, // 200
	SMART_HOST_CMD_TEST2,         // 201
	SMART_HOST_CMD_TEST3,         // 202
	// ... ...
	SMART_HOST_CMD_MAX,           // SMART主机命令最大编号,在最大编号上边新增命令编号
}HOST_CMD;

// 命令处理函数的声明
HOST_CMD_PROCESS_FUNC(HostTest1); // unsigned int HostTest1(int chan, int *pParam, void *pBuf);
HOST_CMD_PROCESS_FUNC(HostTest2); // unsigned int HostTest2(int chan, int *pParam, void *pBuf);

HOST_CMD_PROCESS_FUNC(SmartHostTest1); // unsigned int SmartHostTest1(int chan, int *pParam, void *pBuf);
HOST_CMD_PROCESS_FUNC(SmartHostTest2); // unsigned int SmartHostTest2(int chan, int *pParam, void *pBuf);


// 保存每一条命令信息的数组
HOST_CMD_DETAIL hostCmdDetail[] =
{
	// 0,100
	MAKE_HOST_CMD(10000, 0,	1, 0, 0, 0, (Host_Cmd_Process_Func)HostTest1),
	// 1,101
	MAKE_HOST_CMD(10000, 0,	1, 0, 0, 0, (Host_Cmd_Process_Func)HostTest2),
};

// 保存每一条命令信息的数组
HOST_CMD_DETAIL smartHostCmdDetail[] =
{
	// 0,200
	MAKE_HOST_CMD(10000, 0,	1, 0, 0, 0, (Host_Cmd_Process_Func)SmartHostTest1),
	// 1,201
	MAKE_HOST_CMD(10000, 0,	1, 0, 0, 0, (Host_Cmd_Process_Func)SmartHostTest2),
};

#endif

1.3 sys_cmd.c

c
#include <stdio.h>
#include <stdlib.h>
#include "debug_printf.h"
#include "sys_cmd.h"

int main(int argc, const char *argv[])
{
	// 命令编号与命令的索引
	int cmd = 0;
	int cmdIndex = 0;
	// 传递给命令处理函数的参数
	int arg = 0;
	int pParam = 0;
	_CMD_DATA cmdBuf = {0, '0'};
	
	// 指向命令处理函数的指针变量
	Host_Cmd_Process_Func pFunc = NULL;
	// 指向命令数组的指针变量
	HOST_CMD_DETAIL *pCmdDetail = NULL;
	
	// 命令的相关数量计算
	int host_cmd_num = HOST_CMD_MAX - HOST_CMD_MIN;
	int smart_host_cmd_num = SMART_HOST_CMD_MAX - SMART_HOST_CMD_MIN;
	int host_cmd_func_num = sizeof(hostCmdDetail)/sizeof(hostCmdDetail[0]);
	int smart_host_cmd_func_num = sizeof(smartHostCmdDetail)/sizeof(smartHostCmdDetail[0]);
	HKPRT("HOST_CMD:num %d,func %d\n", host_cmd_num, host_cmd_func_num);
	HKPRT("SMART_HOST_CMD:num %d,func %d\n", smart_host_cmd_num, smart_host_cmd_func_num);
	// 命令的查找与处理
	cmd = atoi(argv[1]);
	if((cmd >= HOST_CMD_MIN) && (cmd < HOST_CMD_MAX))
	{
		arg = 1;
		pParam = 1;
		cmdBuf.a = 100;
		cmdBuf.b = 'a';
		pCmdDetail = hostCmdDetail;
		cmdIndex = cmd - HOST_CMD_MIN;
		if(cmdIndex >= host_cmd_func_num) // 判断一下防止访问数组越界
		{
			HKPRTE("This function is not exist in hostCmdDetail[%d]!!! cmd:%d,cmdIndex:%d\n", host_cmd_func_num, cmd, cmdIndex);
			return -1;
		}
	}
	else if((cmd >= SMART_HOST_CMD_MIN) && (cmd < SMART_HOST_CMD_MAX))
	{
		arg = 2;
		pParam = 2;
		cmdBuf.a = 200;
		cmdBuf.b = 'b';
		pCmdDetail = smartHostCmdDetail;
		cmdIndex = cmd - SMART_HOST_CMD_MIN;
		if(cmdIndex >= smart_host_cmd_func_num) // 判断一下防止访问数组越界
		{
			HKPRTE("This function is not exist in smartHostCmdDetail[%d]!!! cmd:%d,cmdIndex:%d\n", smart_host_cmd_func_num, cmd, cmdIndex);
			return -1;
		}
	}
	else
	{
		HKPRTE("cmd number is invalid!!!\n");
		return -1;
	}
	// 执行命令处理函数
	pFunc = (Host_Cmd_Process_Func)(GET_CMD_FUNC(pCmdDetail[cmdIndex]));	// hostCmdDetail
	if(pFunc == NULL)
	{
		HKPRTE("This function is not exist!!! cmd:%d,cmdIndex:%d,pFunc:%p\n", cmd, cmdIndex, pFunc);
		return -1;
	}
	pFunc(arg, (int *)&pParam, (void *)&cmdBuf);
	
	return 0;
}


//unsigned int HostTest1(int arg, int *pParam, void *pBuf)
HOST_CMD_PROCESS_FUNC(HostTest1)
{
	_CMD_DATA *p = (_CMD_DATA *)pBuf;
	HKPRT("This is HostTest1!!!\n");
	HKPRT("arg=%d,*pParam=%d,pBuf.a=%d,pBuf.b=%c\n", arg, *pParam, p->a, p->b);
	return 0;
}

//unsigned int HostTest2(int arg, int *pParam, void *pBuf)
HOST_CMD_PROCESS_FUNC(HostTest2)
{
	_CMD_DATA *p = (_CMD_DATA *)pBuf;
	HKPRT("This is HostTest2!!!\n");
	HKPRT("arg=%d,*pParam=%d,pBuf.a=%d,pBuf.b=%c\n", arg, *pParam, p->a, p->b);
	return 0;
}

//unsigned int SmartHostTest1(int arg, int *pParam, void *pBuf)
HOST_CMD_PROCESS_FUNC(SmartHostTest1)
{
	_CMD_DATA *p = (_CMD_DATA *)pBuf;
	HKPRT("This is SmartHostTest1!!!\n");
	HKPRT("arg=%d,*pParam=%d,pBuf.a=%d,pBuf.b=%c\n", arg, *pParam, p->a, p->b);
	return 0;
}

//unsigned int SmartHostTest2(int arg, int *pParam, void *pBuf)
HOST_CMD_PROCESS_FUNC(SmartHostTest2)
{
	_CMD_DATA *p = (_CMD_DATA *)pBuf;
	HKPRT("This is SmartHostTest2!!!\n");
	HKPRT("arg=%d,*pParam=%d,pBuf.a=%d,pBuf.b=%c\n", arg, *pParam, p->a, p->b);
	return 0;
}

1.4 debug_printf.h

c
#ifndef __DEBUG_PRINTF_H__
#define __DEBUG_PRINTF_H__

#include <stdio.h>

#define DEBUG_ENABLE  1           /* 是否开启调试信息 */
#define LOG_LEVEL     DEBUG_ALL   /* 调试信息显示级别 */
 
#define ERROR   "ERROR"           /* 错误 */
#define WARN    "WARN "           /* 警告 */
#define INFO    "INFO "           /* 信息 */
/* 颜色定义——字体颜色 */
#define CLS_ALL        "\033[0m"  /* 清除所有颜色 */
#define F_BLACK        "\033[30m" /* 黑色字体 */
#define F_RED          "\033[31m" /* 红色字体 */
#define F_GREEN        "\033[32m" /* 绿色字体 */
#define F_YELLOW       "\033[33m" /* 黄色字体 */
#define F_BLUE         "\033[34m" /* 蓝色字体 */
#define F_PURPLE       "\033[35m" /* 紫色字体 */
#define F_CYAN         "\033[36m" /* 青色字体 */
#define F_WHITE        "\033[37m" /* 白色字体 */
/* 颜色定义——背景颜色 */
#define B_BLACK        "\033[40m" /* 黑色背景 */
#define B_RED          "\033[41m" /* 红色背景 */
#define B_GREEN        "\033[42m" /* 绿色背景 */
#define B_YELLOW       "\033[43m" /* 黄色背景 */
#define B_BLUE         "\033[44m" /* 蓝色背景 */
#define B_PURPLE       "\033[45m" /* 紫色背景 */
#define B_CYAN         "\033[46m" /* 青色背景 */
#define B_WHITE        "\033[47m" /* 白色背景 */
/* 颜色定义——背景的字体 */
#define BLACK_RED      "\033[30;41m" /* 红底黑字 */
#define BLACK_GREEN    "\033[30;42m" /* 绿底黑字 */
#define BLACK_YELLOW   "\033[30;43m" /* 黄底黑字 */

/* 调试等级枚举类型定义 */
enum DEBUG_LEVEL
{
    DEBUG_OFF   = 0,
    DEBUG_ERROR = 1,
    DEBUG_WARN  = 2,
    DEBUG_INFO  = 3,
    DEBUG_ALL   = 4
};
//===========================================================================
/* 自定义类型和字体颜色 */
#define MYDEBUG(TYPE, COLOR, FMT, ARGS...) \
        do \
        { \
            if(DEBUG_ENABLE && (DEBUG_##TYPE <= LOG_LEVEL)) \
            { \
                printf(COLOR); \
                printf("[%s][%s:%s:%d]"FMT, TYPE, __FILE__, __FUNCTION__, __LINE__, ##ARGS); \
                printf(CLS_ALL"\n"); \
            } \
        } while(0)
//===========================================================================
/* 平时调试使用 */
#include <string.h>
#include <time.h> // 时间函数用
#define filename(x) (strrchr(x,'/')?(strrchr(x,'/')+1):x)
#define NORMAL_DEBUG 1 // 注意使用下边两个宏需要自己添加换行,比较符合平时的习惯
// 获取时间函数
char *timeString(void)
{
  struct timespec ts;
  clock_gettime(CLOCK_REALTIME, &ts);
  struct tm *timeinfo = localtime(&ts.tv_sec);
  static char timeString[30];
  sprintf(timeString, "%.2d-%.2d %.2d:%.2d:%.2d", 
          timeinfo->tm_mon + 1, 
          timeinfo->tm_mday, 
          timeinfo->tm_hour, 
          timeinfo->tm_min, 
          timeinfo->tm_sec);
  return timeString;
}
// 获取系统时间戳 单位 ms
int sys_pts_get_ms(void)
{
    struct timespec tv;
    long long lasttime = 0;

    clock_gettime(CLOCK_MONOTONIC, &tv);
    lasttime = tv.tv_sec * 1000 + tv.tv_nsec / (1000 * 1000);
    return lasttime;

}

#define HKPRT(fmt...) \
        do \
        { \
            if(NORMAL_DEBUG) \
            { \
                printf(F_YELLOW); \
                printf("[%s][HK_LOG][%s:%d][%s]", timeString(), filename(__FILE__), __LINE__, __FUNCTION__); \
                printf(CLS_ALL); \
                printf(fmt); \
            } \
        } while(0)
#define HKPRTE(fmt...) \
        do \
        { \
            if(NORMAL_DEBUG) \
            { \
                printf(F_RED); \
                printf("[%s][HK_LOG][%s:%d][%s]", timeString(), filename(__FILE__), __LINE__, __FUNCTION__); \
                printf(CLS_ALL); \
                printf(fmt); \
            } \
        } while(0)

#endif

2. 测试结果

我们通过以下命令编译:

shell
gcc sys_cmd.c -Wall

运行结果如下:

c
sumu@virtual-machine:~/6temp/test$ ./a.out 101
[02-12 17:55:56][HK_LOG][01_sys_cmd.c:37][main]HOST_CMD:num 3,func 2
[02-12 17:55:56][HK_LOG][01_sys_cmd.c:38][main]SMART_HOST_CMD:num 3,func 2
[02-12 17:55:56][HK_LOG][01_sys_cmd.c:100][HostTest2]This is HostTest2!!!
[02-12 17:55:56][HK_LOG][01_sys_cmd.c:101][HostTest2]arg=1,*pParam=1,pBuf.a=100,pBuf.b=a
sumu@virtual-machine:~/6temp/test$ ./a.out 102
[02-12 17:55:59][HK_LOG][01_sys_cmd.c:37][main]HOST_CMD:num 3,func 2
[02-12 17:55:59][HK_LOG][01_sys_cmd.c:38][main]SMART_HOST_CMD:num 3,func 2
[02-12 17:55:59][HK_LOG][01_sys_cmd.c:51][main]This function is not exist in hostCmdDetail[2]!!! cmd:102,cmdIndex:2
sumu@virtual-machine:~/6temp/test$ ./a.out 103
[02-12 17:56:03][HK_LOG][01_sys_cmd.c:37][main]HOST_CMD:num 3,func 2
[02-12 17:56:03][HK_LOG][01_sys_cmd.c:38][main]SMART_HOST_CMD:num 3,func 2
[02-12 17:56:03][HK_LOG][01_sys_cmd.c:71][main]cmd number is invalid!!!
sumu@virtual-machine:~/6temp/test$ ./a.out 201
[02-12 17:56:08][HK_LOG][01_sys_cmd.c:37][main]HOST_CMD:num 3,func 2
[02-12 17:56:08][HK_LOG][01_sys_cmd.c:38][main]SMART_HOST_CMD:num 3,func 2
[02-12 17:56:08][HK_LOG][01_sys_cmd.c:118][SmartHostTest2]This is SmartHostTest2!!!
[02-12 17:56:08][HK_LOG][01_sys_cmd.c:119][SmartHostTest2]arg=2,*pParam=2,pBuf.a=200,pBuf.b=b
sumu@virtual-machine:~/6temp/test$ ./a.out 203
[02-12 17:56:10][HK_LOG][01_sys_cmd.c:37][main]HOST_CMD:num 3,func 2
[02-12 17:56:10][HK_LOG][01_sys_cmd.c:38][main]SMART_HOST_CMD:num 3,func 2
[02-12 17:56:10][HK_LOG][01_sys_cmd.c:71][main]cmd number is invalid!!!