码迷,mamicode.com
首页 > 编程语言 > 详细

写给过去的自己-No.2-数据结构篇-初尝柔性数组

时间:2015-05-20 18:16:44      阅读:194      评论:0      收藏:0      [点我收藏+]

标签:

    过去你的自己,你好。
    照顾宝宝,写完第一篇就没什么时间,既然上次讲的就是数据结构,这次也讲点相关的。
    其实接触柔性数组也是个比较奇妙的过程,你以后会遇到个学长,毕业后从事软件行业,在中兴锻炼过,将会给你很多软件方面的启发。一次讨论结构体内数据结构 类型强制转换的问题时(剧透一下,是你用的编译器中的一个bug导致了ARM操作非对齐的地址所以进入hardfault,这个问题和今天讲的没关系,以 后再开题),听到了“柔性数组”这个关键字,当时没听明白也就没有去深究。然后某日在论坛看到篇好帖讲C语言的一些高级用法,看到了一个疑似“柔性数组” 的说明,细究之后恍然大悟原来柔性数组是这么回事。不过另一个问题来了,感觉没有什么场合会用到,也就没有深入下去。结果就在最近,在做模块化编程时,发 现了用柔性数组极佳的环境。我就是这样开始正视柔性数组。开篇讲了这么多废话的目的是要告诉你:1、多向前辈学习;2、注意前辈不经意吐露的“关键 字”;3、有机会一定要深究“关键字”,会有很大收获;4、对新的知识再深究下应用环境。
    数组一般都是静态的,所谓静态就是在编译之时就已经确定的。如果是全局数组,则数组的大小及存放在内存中的位置就确定了(静态储存区);若是局部数组且编 译器不支持VLA(可变长度数组),那该数组在内存的位置就在运行时分配(栈区域),但是数组的大小还是在编译之时就会确定下来(若编译器支持C99的 VLA,则仅限局部数组可使用可变长度)。从上面两种情况可以看出,不管数组最终存放在哪里,数组的大小是需要在编译时确定的(C语言中规定申请数组时中 括号内的数据必须是常量),那问题就来了,如果你想开发一个软件模块且日后不想根据不同的应用修改代码,那用我们上述方法的数组肯定是不行的,因为不知道 模块在被使用时数组的大小(其实可以简单的方法解决--宏定义数组大小,有机会再解释)。
    上述就是问题的提出,下面介绍几个豆知识:
     1、int a = 0;那a就是变量名,其实就是内存的一个别名。若a的地址为0x200000FE,则a就是内存0x200000FE的别名
     2、struct t
          {
               unsigned char wLen;
               unsigned char awData[7];
           };

结构体t的大小为8个字节,如果t申请在内存为0x20000000的内存上,那t.awData[6]的地址位0x20000007上,t.awData[7]的地址为0x20000008,即结构体t所有成员所占内存的后面一个字节内存。

    再回到静态/动态数组上来,目测静态数组不能满足模块化编程的需要,那就看看有没有动态数组。既然是动态,那就是在设备运行时进行操作,而数组又是一种数 据结构,也需要分配内存,这样很容易就想到“动态分配malloc”来解决数组大小既定的问题。看一下下面的结构体。

1      typedef struct _tTest_
2     {
3         unsigned char wLen;      /* Length of data     */
4         unsigned char awData[0]; /* Data field         */
5     }T_TEST;
6 
7     T_TEST t = {0};              /* Create a "t"       */

 

    依旧是类似的结构体t,不同的是成员数组awData的大小为0,通过sizeof (T_TEST)可以发现结构体t的大小为1 byte,说明数组awData不占内存,那这个数组还有什么意义呢?看一下豆知识1,变量名实际上就是内存地址的别名,那么这个长度为0的数组实际上就 是某个内存地址的一个别名。那t.awData[0]是哪个内存地址的别名呢?从豆知识2可知它代表的是结构体t后面的一个byte内存,依次类 推,awData[9]就是这个结构体t后面第10个byte的内存。如果结构体t后面的N个byte内存都不被其他变量使用,那t后面的N个byte内 存就可视为数组awData的可用范围,即awData数组的长度为N个字节。也就是说,如果你能控制N的大小,你就能自由地控制数组awData的大 小,那如何在运行过程中动态地控制这个N呢?使用malloc。

1     T_TEST *ptTest = NULL;
2     int     wLen   = 10;                             /* Get 10-byte length             */
3 
4     ptTest = (T_TEST*)malloc(sizeof(T_TEST) + wLen); /* Create the space for array     */
5 
6     if(ptTest != NULL)
7     {
8         ptTest->awData[9] = 10;                      /* Operate the last byte of array */
9     }

    上例中就是通过malloc在内存的堆区域中申请了一块内存区域,这个区域由两部分组成,一个是T_TEST结构体的大小(1 byte),另一个就是数组的长度wLen。通过这个方法可以确保结构体后面的wLen个内存是不会被其他变量占用的,这样数组就可以安全地使用。当数组 用完以后,可以free释放该块内存,再根据具体要求修改wLen重新申请,这样这个数组的长度就变得可变了,这就是柔性数组,既不用担心数组定义过大而 浪费空间,又不会因为数组定义过小而发生越界操作。
    再谈谈实际的应用场景,我开发的某个软件模块中有两个逻辑运算功能,于是我将可以对这个模块的数据结构做如下定义

 1     typedef _LOGIC_
 2     {
 3         int wLogicOp;      /* Logic operation type: AND/OR/XOR */
 4         int wLogicInputA;  /* Logic input value A              */
 5         int wLogicInputB;  /* Logic input value B              */
 6     }T_LOGIC;
 7 
 8     typedef _MODULE_FUNC_
 9     {
10         int     wFuncEn;      /* Enable or disable logic func */
11         int     wLogicOutput; /* Output operation of logic    */
12         T_LOGIC tLogic1;      /* Logic func 1                 */
13         T_LOGIC tLogic2;      /* Logic func 2                 */
14     }T_MODULE_FUNC;

结构体T_LOGIC定义了逻辑运算的基本单元,包含了逻辑运算的操作(与或非等)和逻辑输入,而结构体T_MODULE_FUNC则是软件模块所定义的 数据结构,可以看出软件模块定义了两个逻辑运算基本单元tLogic1和tLogic2,tLogic1的运算结果将作为tLogic2的输入继续执行下 一级逻辑运算。如果说产品的需求就是两级的逻辑运算,那这个软件模块是可以满足要求,但如果说产品需求是三级逻辑运算,那原有的数据结构就不适用,代码也 要改,则模块的复用性就差,所以不能定义个tLogic3来解决该问题。为了解决这个问题,我们可以把原来的结构修改成这样:

1     typedef _MODULE_FUNC_
2     {
3         int     wFuncEn;              /* Enable or disable logic func */
4         int     wLogicOutput;         /* Output operation of logic    */
5         T_LOGIC tLogic[NUM_OF_LOGIC]; /* Logic unit                   */
6     }T_MODULE_FUNC;

这样软件模块通过宏定义NUM_OF_LOGIC的数值就可以我需要多少级的逻辑运算,这种方法实现比较简单,也易于理解,但有时候在整体代码框架下这个 宏定义的位置需要考虑清楚,同时要关心头文件的包含顺序。为了让模块与其它模块更少耦合,让模块更好用,可以在这里使用柔性数组:

1     typedef _MODULE_FUNC_
2     {
3         int     wFuncEn;      /* Enable or disable logic func */
4         int     wLogicOutput; /* Output operation of logic    */
5         T_LOGIC tLogic[0];    /* Logic unit                   */
6     }T_MODULE_FUNC;

这样就可以不用事先定义这个NUM_OF_LOGIC宏定义,而在运行过程中动态的申请和使用,是不是很方便呢(其实很多情况下还是宏定义方便,呵呵)。

终于还是写完了,这篇写了好长时间,有了宝宝之后属于自己的时间少且零散,劝你还是在萌萌降临之前多多学习,我也尽可能的多总结点东西给你,希望你能收的到。

写在最后:借个地方向你感叹一下生活,你以后某天会放弃一个多年的向往,很多时候你会面临抉择,哪一个选项都伴随着牺牲,你自己要考虑清楚什么更重要。当 你遇到这样的纠结时,安慰自己,根本就没有那个选择,你在做的是你认为最重要的。我不想干预你太多想法,但记住一点,你有家庭的责任,请以整个家庭的角度 考虑问题。

写给过去的自己-No.2-数据结构篇-初尝柔性数组

标签:

原文地址:http://www.cnblogs.com/ianhom/p/4517704.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!