Chinaunix首页 | 论坛 | 博客
  • 博客访问: 106150
  • 博文数量: 32
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 318
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-30 09:58
个人简介

经过长期对C语言的研究,目前只有两个方面不懂:这也不懂,那也不懂。 本人擅长 Ai、Fw、Fl、Br、Ps 等软件的安装卸载,精通 CSS、Java、PHP、C、C++、C#、Java、Ruby、Python、Objective-C等单词的拼写,熟悉 Windows、Linux、Mac、Android、iOS等系统的开关机 https://github.com/ianhom

文章分类

全部博文(32)

我的朋友

分类: C/C++

2015-04-20 16:20:35

    过去你的自己,你好。
    照顾宝宝,写完第一篇就没什么时间,既然上次讲的就是数据结构,这次也讲点相关的。
    其实接触柔性数组也是个比较奇妙的过程,你以后会遇到个学长,毕业后从事软件行业,在中兴锻炼过,将会给你很多软件方面的启发。一次讨论结构体内数据结构类型强制转换的问题时(剧透一下,是你用的编译器中的一个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. 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. ptTest = (T_TEST*)malloc(sizeof(T_TEST) + wLen); /* Create the space for array     */

  4. if(ptTest != NULL)
  5. {
  6.     ptTest->awData[9] = 10;                      /* Operate the last byte of array */
  7. }
    上例中就是通过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. typedef _MODULE_FUNC_
  8. {
  9.     int     wFuncEn;      /* Enable or disable logic func */
  10.     int     wLogicOutput; /* Output operation of logic    */
  11.     T_LOGIC tLogic1;      /* Logic func 1                 */
  12.     T_LOGIC tLogic2;      /* Logic func 2                 */
  13. }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宏定义,而在运行过程中动态的申请和使用,是不是很方便呢(其实很多情况下还是宏定义方便,呵呵)。

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

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