指针结构体一直是我的盲点,所以今天有必要整“清理门户”。此种通过一个函数操作一个结构体,实现对应函数功能,用法十分巧妙,使用得当可以使得代码可移植性和易懂性大大的增加,有人说过“代码注释的最高境界是程序的自述,而不是双斜杠然后后面跟着中英文的注释”。哈哈,说远了,下面开始进入今天的加油站,补充体力了。
1 // 头文件 2 #include <stdio.h> 3 4 // 函数声明 5 typedef struct _halDeviceFuncs_t 6 { 7 void (*pfnInit)(int task_id); 8 void (*pfnLowInit)(void); 9 } halDeviceFuncs_t; 10 11 typedef struct _sample_t 12 { 13 int a; 14 int b; 15 } sample_t; 16 17 sample_t s; 18 19 // s.a s.b 20 21 // #define HAL_DEVICE_FUNCS(_label_, _init_, _low_init_) halDeviceFuncs_t DEV_FUNCS_##_label_ = {_init_, _low_init_}; 22 // HAL_DEVICE_FUNCS(TEST, a, b) 23 // halDeviceFuncs_t DEV_FUNCS_TEST = {a, b}; 24 25 #define HAL_DEVICE_FUNCS(_label_, _init_, _low_init_) halDeviceFuncs_t DEV_FUNCS_##_label_ = {_init_, _low_init_}; 26 27 28 29 /*dev 1*/ 30 void init_1(int a); 31 void lowinit_1(void); 32 33 void init_1(int a) 34 { 35 printf("init_1 func %d\n", a); 36 } 37 38 void lowinit_1(void) 39 { 40 printf("lowinit_1 func\n"); 41 } 42 43 HAL_DEVICE_FUNCS(DEV_1, init_1, lowinit_1) 44 45 // halDeviceFuncs_t DEV_FUNCS_DEV_1 = {init_1, lowinit_1}; 46 47 /*dev 2*/ 48 void init_2(int a); 49 void lowinit_2(void); 50 51 void init_2(int a) 52 { 53 printf("init_2 func %d\n", a); 54 } 55 56 void lowinit_2(void) 57 { 58 printf("lowinit_2 func\n"); 59 } 60 61 HAL_DEVICE_FUNCS(DEV_2, init_2, lowinit_2) 62 63 /*dev 3*/ 64 void init_3(int a); 65 void lowinit_3(void); 66 67 void init_3(int a) 68 { 69 printf("init_3 func %d\n", a); 70 } 71 72 void lowinit_3(void) 73 { 74 printf("lowinit_3 func\n"); 75 } 76 77 HAL_DEVICE_FUNCS(DEV_3, init_3, lowinit_3) 78 79 /*global*/ 80 #define HAL_DEVICE_FUNCS_EXPORT(_label_) extern halDeviceFuncs_t DEV_FUNCS_##_label_; 81 82 HAL_DEVICE_FUNCS_EXPORT(DEV_1) 83 HAL_DEVICE_FUNCS_EXPORT(DEV_2) 84 HAL_DEVICE_FUNCS_EXPORT(DEV_3) 85 86 static const halDeviceFuncs_t* dev_list[] = 87 { 88 &DEV_FUNCS_DEV_1, 89 &DEV_FUNCS_DEV_2, 90 &DEV_FUNCS_DEV_3 91 }; 92 93 static const dev_list_len = (sizeof(dev_list) / sizeof(halDeviceFuncs_t*)); 94 95 // 主函数 96 void main() 97 { 98 int i; 99 100 printf("start dev number %d!\n", dev_list_len); 101 102 for (i = 0; i < dev_list_len; i++) 103 { 104 dev_list[i]->pfnLowInit(); 105 dev_list[i]->pfnInit(i+1); 106 } 107 108 //funcs 109 }
其中的代码部分,可以直接放置在C语言的编译环境,编译即可产生结果。
要想理解整个程序的,需要知道如下几点
1.结构体指针变量的定义,定义结构体的变量的一般形式有三种。其中“指针变量名”为指针结构体变量的名称。形式1是先定义结构体,然后在定义此类型的结构体指针;形式2和形式3是在定义结构体的同时定义此类型的结构体指针变量。
1 形式1:先定义结构体类型,再定义变量 2 struct结构体标识符 3 { 4 成员变量列表;… 5 }; 6 struct 结构体标识符 *指针变量名; 7 8 变量初始化一:struct结构体标识符 变量名={初始化值1,初始化值2,…, 初始化值n }; 9 10 11 形式2:在定义类型的同时定义变量 12 struct结构体标识符 13 { 14 成员变量列表;… 15 } *指针变量名; 16 17 变量初始化二: 18 19 20 形式3:直接定义变量,用无名结构体直接定义变量只能一次 21 struct 22 { 23 成员变量列表;… 24 }*指针变量名;
知道以上几点,下面就可以对与整个程序进行把握了,个人的理解是这样的
1.遇到所有问题,先从宏观的角度去思考,大方向的先把握下“我宏观的想做什么,为什么想做,现在我在做什么,现在做的事情有什么用处”。我宏观的想把把嵌入式开发这件事情做好(理想报复,先积累两年再谈理想),如果我现在都不能把嵌入式开发或者说是现在的这份工作做好,如何进入下面的产品开发和生存?我现在就在做开发过程中遇到的C语言部分的一个小tip,如果我现在把这个小tip学会之后,对于理解现有代码大有帮助并且可以对于我重新构思代码架构有着不可或缺的作用。哈哈,说了这么多,精神层次没有问题了
2.进入到程序的整体构思,现在有好多函数的初始化,好多函数名容易忘记,操作过程中需要到程序中去找,比较麻烦,现在有个小tip去解决“一键初始化”。先看结构体
1 typedef struct _halDeviceFuncs_t 2 { 3 void (*pfnInit)(int task_id); 4 void (*pfnLowInit)(void); 5 } halDeviceFuncs_t;
看完之后看主函数
1 // 主函数 2 void main() 3 { 4 int i; 5 6 printf("start dev number %d!\n", dev_list_len); 7 8 for (i = 0; i < dev_list_len; i++) 9 { 10 dev_list[i]->pfnLowInit(); 11 dev_list[i]->pfnInit(i+1); 12 } 13 14 //funcs 15 }
想这种方式初始化,把所有的需要Init的设备通过这种方式,一步到位,多简洁,究竟如何实现的呢?看下面
#define HAL_DEVICE_FUNCS(_label_, _init_, _low_init_) halDeviceFuncs_t DEV_FUNCS_##_label_ = {_init_, _low_init_};
可能不太理解上面的宏定义的意思吧,要是能理解最好,不理解也没事,进入下面具体设备中来
1 /*dev 1*/ 2 void init_1(int a); 3 void lowinit_1(void); 4 5 void init_1(int a) 6 { 7 printf("init_1 func %d\n", a); 8 } 9 10 void lowinit_1(void) 11 { 12 printf("lowinit_1 func\n"); 13 } 14 15 HAL_DEVICE_FUNCS(DEV_1, init_1, lowinit_1) 16 17 // halDeviceFuncs_t DEV_FUNCS_DEV_1 = {init_1, lowinit_1};
设备2和设备3与设备1类似,“HAL_DEVICE_FUNCS(DEV_1, init_1, lowinit_1)”
通过上面的宏定义,直接取代了下面的这种笨方法的定义,简单快捷不用刻意的去思考函数的命名格式,“halDeviceFuncs_t DEV_FUNCS_DEV_1 = {init_1, lowinit_1}”
好了,函数定义好了,我们开始引入函数(大多数函数的定义部分和引用部分不在同一个文件下),然后进行操作了
引入函数我们当然也不能落后与笨方法,直接宏定义的引入函数方法
1 /*global*/ 2 #define HAL_DEVICE_FUNCS_EXPORT(_label_) extern halDeviceFuncs_t DEV_FUNCS_##_label_; 3 4 HAL_DEVICE_FUNCS_EXPORT(DEV_1) 5 HAL_DEVICE_FUNCS_EXPORT(DEV_2) 6 HAL_DEVICE_FUNCS_EXPORT(DEV_3)
这样可以取代下面的引入方法
extern halDeviceFuncs_t DEV_FUNCS_DEV1 extern halDeviceFuncs_t DEV_FUNCS_DEV2 extern halDeviceFuncs_t DEV_FUNCS_DEV3
好了,有些混乱了吧,不要着急,回到我们开始说的问题上,千万别忘记我们的目的了,我们想采用下面简单的这种方法初始化的
1 for (i = 0; i < dev_list_len; i++) 2 { 3 dev_list[i]->pfnLowInit(); 4 dev_list[i]->pfnInit(i+1); 5 }
总得为这中简单方法的初始化做些数组的定义吧
1 static const halDeviceFuncs_t* dev_list[] = 2 { 3 &DEV_FUNCS_DEV_1, 4 &DEV_FUNCS_DEV_2, 5 &DEV_FUNCS_DEV_3 6 }; 7 8 static const dev_list_len = (sizeof(dev_list) / sizeof(halDeviceFuncs_t*));
有了函数list,自动计算长度,初始化部分压根不用去理会,直接把list里面的内容全部初始化的干干净净,不需要我们去担心哪个忘记初始化了,因为我们有list
OK,到此此种小tip的代码分析完了,下面我们开始进入到总结阶段,假如我们现在想让程序猿B增加一个设备4,需要做的事情有如下几个
1.程序猿B完成下面的函数
1 /*dev 4*/ 2 void init_3(int a); 3 void lowinit_4(void); 4 5 void init_4(int a) 6 { 7 printf("init_4 func %d\n", a); 8 } 9 10 void lowinit_4(void) 11 { 12 printf("lowinit_4 func\n"); 13 } 14 15 HAL_DEVICE_FUNCS(DEV_4, init_4, lowinit_4)//将设备4归为此种结构体旗下
2.程序猿A完成下面的事情
HAL_DEVICE_FUNCS_EXPORT(DEV_3)//哪里用就在哪里引用 static const halDeviceFuncs_t* dev_list[] = { &DEV_FUNCS_DEV_1, &DEV_FUNCS_DEV_2, &DEV_FUNCS_DEV_3, &DEV_FUNCS_DEV_4 //list下面增加设备4就行,初始化的时候程序会根据自动计算出的长度把4给初始化喽 };
这样程序猿A和B就把整个程序合作完成了,程序猿A负责把代码的整个框架构思好,程序猿B负责写驱动,就这样一个工程就被瓜分出去了,就算增加设备5,程序猿A可以将设备5分给C、D、E、F人做,缩小了产品的研发时间,增加了团队的合作效率。
哈哈,终于把整个程序的思路理清楚了,有的地方可能还需要修改,等我想修改再修改吧,吃饭去了,马上上班的时间到了,祝君好运!