通过一个函数,操作一个结构体,实现对应函数功能

指针结构体一直是我的盲点,所以今天有必要整“清理门户”。此种通过一个函数操作一个结构体,实现对应函数功能,用法十分巧妙,使用得当可以使得代码可移植性和易懂性大大的增加,有人说过“代码注释的最高境界是程序的自述,而不是双斜杠然后后面跟着中英文的注释”。哈哈,说远了,下面开始进入今天的加油站,补充体力了。

  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人做,缩小了产品的研发时间,增加了团队的合作效率。

哈哈,终于把整个程序的思路理清楚了,有的地方可能还需要修改,等我想修改再修改吧,吃饭去了,马上上班的时间到了,祝君好运!

时间: 2024-10-02 01:47:29

通过一个函数,操作一个结构体,实现对应函数功能的相关文章

驱动学习之register_chrdev_region函数和cdev结构体

1:register_chrdev_region int register_chrdev_region(dev_t from, unsigned count, const char *name) {     struct char_device_struct *cd;     dev_t to = from + count;     dev_t n, next;     for (n = from; n < to; n = next) {         next = MKDEV(MAJOR(n

c语言,结构体里面的函数

以linux-3.2内核代码为例,结构体里面的函数的用法: 例,在某驱动文件中,定义了一个平台设备驱动: static struct platform_driver s3c24xx_led_driver = { .probe = s3c24xx_led_probe, .remove = s3c24xx_led_remove, .driver = { .name = "s3c24xx_led", .owner = THIS_MODULE, }, }; 对struct platform_d

(struct)结构体变量作为函数参数调用的方法小结

结构体变量.结构指针变量.结构数组作为函数的参数应用实例分析 struct stud { long int num; float score; }; /*结构体变量作为函数的参数,修改之后的成员值不能返回到主调函数*/ void funvr(struct stud t) { t.num=2000101; t.score=71.0; } /*结构体数组作为函数的参数,修改后的元素的成员值能返回到主调函数*/ void funar(struct stud t[]) //void funar(stru

【C语言】使用结构体和malloc函数时的一些错误。

使用结构体错误: #include <stdio.h> struct STU { char *name; int score; }stu,*pstu; int main () { strcpy(stu.name,"bit-tech"); strcpy(pstu->name,"bit-tech"); return 0; } 错误一:strcpy(stu.name,"bit-tech"); 结构体中的成员name是一个指针,声明结构

C/C++ 学习之旅 - 实战3 - 在Struct结构体中使用函数Function

Coding部分: #include<stdio.h> struct Student{ int ID; char* Name; int Age; void(*toString)(int id,char* name,int age); };//定义包含学生ID.姓名.年龄和输出函数指针的结构体Student void toString(int id,char* name,int age){ printf("myStudent实例变量的ID值:%d\n",id); printf

结构体中定义函数指针

转自:http://blog.csdn.net/unix21/article/details/9293877 结构体指针变量的定义,定义结构体变量的一般形式如下: 形式1:先定义结构体类型,再定义变量 struct结构体标识符 { 成员变量列表;… }; struct 结构体标识符 *指针变量名; 变量初始化一:struct结构体标识符 变量名={初始化值1,初始化值2,…, 初始化值n }; 形式2:在定义类型的同时定义变量 struct结构体标识符 { 成员变量列表;… } *指针变量名;

C语言结构体中的函数指针

这篇文章简单的叙述一下函数指针在结构体中的应用,为后面的一系列文章打下基础 本文地址:http://www.cnblogs.com/archimedes/p/function-pointer-in-c-struct.html,转载请注明源地址. 引言 指针是C语言的重要组成部分, 于是深入理解指针并且高效地使用指针可以使程序员写出更加老练的程序.我们要记住指针是一个指向内存地址的变量.指针可以引用如int.char……常见的数据类型,例如: int * intptr; // 声明一个指向整型值的

Golang中函数结构体,将函数转换为接口

函数结构体,将函数转换为接口 定义一个函数类型 F,并且实现接口 A 的方法,然后在这个方法中调用自己.这是 Go 语言中将其他函数转换为接口 A 的常用技巧(参数返回值定义与 F 一致) 实现一个动态生成的"回调函数",比如缓存中,当key不存在,则需要从数据库或文件等远程数据源中取数据.所以回调函数不能写死. 采用用户自定义回调函数的方法,因此,在缓存的数据结构中,有一个成员方法变量为回调函数. 下面这段代码就是一个示例. type Getter interface { Get(k

关于结构体变量作为函数的形参研究

#include<stdio.h> typedef struct { int b,p ;}A; void f(A c) { int j; c.b+=1;c.p+=2;} main() { int i; A a={1,2}; f(a); printf("%d%d\n",a.b,a.p); } 结构体变量作为函数的形参即void f(A a)时候,在调用函数中改变形参值对于调用的函数中的实参没有任何影响,即输出的依然是实参.

结构体,结构体指针作为函数参数的应用笔记

1. 结构体,结构体指针作为函数参数有何区别 #include <stdio.h> #include <string.h> struct animal { char name[30]; int num; }; //使用结构体作为参数 浪费内存 需要建立结构体 void change_struct(struct animal cat) { cat.num = 17; } //函数内部改变需要地址 所以需要指针保存 void change_point(struct animal *ca