您的位置:首页 > 编程语言 > C语言/C++

一种典型C语言开发思维及其可能的问题

2012-09-08 23:51 204 查看
先来看一个开发实例:

静态内存模块主要功能是根据指定的(模块号,所需内存大小)从某个固定的区域划分内存,在运行过程中不会有申请释放操作。

如有一块5M的内存空间addr,分别有5个模块,每个模块所需的内存为(1MB,2MB,1MB,0.5MB,0.5MB),则各模块分到的地址如下

(addr,addr+1MB, addr+3MB, addr+4MB,addr+4.5MB)。当然还有其他的一些扩展功能如对齐,模块间隔离等等。

开始的实现时,先定义一个配置表结构体,结构体有如下成员:模块号,模块要求内存大小,分配到的地址,其他如对齐要求等。

然后定义一个配置表的全局变量gastrCfgTbl,初始化函数里根据这些配置信息进行内存划分,初始化函数如下:

int static_mem_init(void *pulStaticMem, unsigned long ulSize)

{

static_mem_cfg_tbl *pstrCfgTbl = gastrCfgTbl;

...

//按照配置表信息进行内存划分。

}

void *static_mem_alloc(int ulModule)

{

static_mem_cfg_tbl *pstrCfgTbl = gastrCfgTbl;

...

//返回已经划分好的配置表内存地址。

return pstrCfgTbl->addr;

}

这个设计初看并没有什么问题,功能也完成的很好。

但过了不久,新的需求来了(可恶,为什么需求老变),希望增加一个新的配置表,在另外一块静态内存空间里进行划分(为什么这个配置表与

老的配置表不能合并,不在本话题讨论之内)。

由于已有的函数与全局配置表信息是耦合在一起的,代码没有变法重用。怎么办呢?很简单,把原来代码里写死的全局变量抽取出来,作为一

个函数的参数传递进去,如下:

int static_mem_init(void *pulStaticMem, unsigned long ulSize, static_mem_cfg_tbl *pstrCfgTbl)

{

//按照配置表信息进行内存划分。

}

void *static_mem_alloc(static_mem_cfg_tbl *pstrCfgTbl, int ulModule)

{

//通过配置表获取模块对应的地址并返回。

}

配置表1和2分别调用static_mem_init并把当前配置表信息传入,代码就从数据耦合变成了接口耦合(接口使用了配置表结构)了,耦合程度降

低了。如下:

int config_table_xxx_init(void)

{

return static_mem_init( static_mem_xxx, xxx_size, pstrCfgTblxxx);

}

int config_table_yyy_init(void)

{

return static_mem_init( static_mem_yyy, yyy_size, pstrCfgTblyyy);

}

修改后的静态内存模块是一个抽象的实现,可以实现重用,增加新的需求也非常简单。比如要求静态内存不是在代码的配置表写死的,而是通

过某个配置文件读取,改动非常简单,增加一个读取配置文件的函数,把配置信息读取出来后传给static_mem_init。

int config_static_mem_by_xml(const char *xml)

{

get_cfg_from_file(xml, cfg_tbl);

return static_mem_init( static_mem_addr, size, cfg_tbl);

}

从这个例子可以看到,一种很典型的C语言开发思维:先设计这个模块的结构体,定义这个结构体的全局变量,然后的功能代码就围绕着这个全

局变量开展。这是很过程式的一种思维,为了完成目标,按照步骤1,2,3开展,最后目标达到了,但带来的问题就是代码不够内聚,存在数据

耦合,不能重用。

而修改后的代码,熟悉面向对象的可能感觉非常眼熟,比如:

class MyClass

{

...

}

int init_x(void)

{

object = new MyClass(x)

object->do_something();

}

int init_y(void)

{

object = new MyClass(y)

object->do_something();

}

从这个实例,给我们带来的启示就是:

1,需求一定要做抽象,需求是一个具体的应用,是易变的,僵化的,不能一拿到需求就冲着目标完成了事。

2,逻辑功能实现与应用分开。记得学习数据结构时,有一个说法是代码=数据结构+算法,全局变量是数据结构的一种实体,不是一个抽象的概

念。因此,在开发时应该抑制全局变量的使用冲动。代码的实现(算法)围绕着抽象的数据结构展开,而不是围绕一个具体的全局变量展开。

先把逻辑功能实现了,再在上面添加具体的应用。这样在实现逻辑功能时,只需要考虑实现上的正确性,而不需要考虑具体应用上的细枝末节

,代码的正确性更容易保证;到了具体应用时,只需要考虑应用场景,不需要考虑逻辑功能实现,也更能把握应用场景的准确性。

3,抑制全局变量的使用冲动。这里感觉有必要再重复一次,如果在开发逻辑功能代码时,发现需要添加全局变量才能解决问题,则需要考虑下

代码的设计是否足够抽象,是否非加不可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: