LV01-13-C语言-函数指针的应用

本文主要是C语言基础——函数指针的应用相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
SecureCRT Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日
开发板 正点原子 i.MX6ULL Linux阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
STM32开发板 正点原子战舰V3(STM32F103ZET6)
点击查看本文参考资料
参考方向 参考原文
------
点击查看相关文件下载
--- ---

前边在《LV01-06-C语言-指针》中的第五部分已经学习了函数指针的概念和基本的应用,本篇笔记主要是函数指针的应用,在这里需要完成一个命令执行的过程。

1.命令处理函数

1.1函数定义

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

1
2
3
4
unsigned int func_name(int arg, int *pParam, void *pBuf)
{
// ... ...
}

【参数说明】

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

1.2函数声明

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

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

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

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

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

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

这样可能会报以下问题:

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

【声明实例】

1
HOST_CMD_PROCESS_FUNC(HostTest1);

这就等价于:

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

1.3函数指针定义

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

1
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 类型,含有三个参数的函数。

【使用实例】

1
2
3
4
5
Host_Cmd_Process_Func pFunc = NULL;

pFunc = (Host_Cmd_Process_Func)HostTest1;

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

2.命令信息

2.1命令描述

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

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

2.2命令信息组合

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

1
2
3
4
5
6
7
// 命令数组的成员组合——将数据组合,方便存放在命令数组中
#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_ \
}

2.3命令数组定义

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 保存每一条命令信息的数组
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),
};

2.4命令编号定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 命令的编号——每个命令的编号
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;

2.5命令区分

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

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

3.使用实例

3.1 sys_cmd_info.h

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#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

3.2 sys_cmd.h

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#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

3.3 sys_cmd.c

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#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;
}

3.4 debug_printf.h

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/** =====================================================
* Copyright © hk. 2022-2025. All rights reserved.
* File name : test.c
* Author : qidaink
* Date : 2022-11-08
* Version :
* Description: 里边的函数定义最好还是写到C文件中,这里只是为了方便
* Others :
* Log :
* ======================================================
*/
#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

3.5 测试结果

我们通过以下命令编译:

1
gcc sys_cmd.c -Wall

运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
hk@vm:~/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
hk@vm:~/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
hk@vm:~/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!!!
hk@vm:~/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
hk@vm:~/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!!!