一种典型C语言开发思维及其可能的问题
来源:互联网 发布:舞蹈女生 知乎 编辑:程序博客网 时间:2024/05/30 23:03
先来看一个开发实例:
静态内存模块主要功能是根据指定的(模块号,所需内存大小)从某个固定的区域划分内存,在运行过程中不会有申请释放操作。
如有一块5M的内存空间addr,分别有5个模块,每个模块所需的内存为(1MB,2MB,1MB,0.5MB,0.5MB),则各模块分到的地址如下
(addr,addr+1MB, addr+3MB, addr+4MB,addr+4.5MB)。当然还有其他的一些扩展功能如对齐,模块间隔离等等。
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);
}
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开展,最后目标达到了,但带来的问题就是代码不够内聚,存在数据
耦合,不能重用。
{
...
}
int init_x(void)
{
object = new MyClass(x)
object->do_something();
}
int init_y(void)
{
object = new MyClass(y)
object->do_something();
}
念。因此,在开发时应该抑制全局变量的使用冲动。代码的实现(算法)围绕着抽象的数据结构展开,而不是围绕一个具体的全局变量展开。
先把逻辑功能实现了,再在上面添加具体的应用。这样在实现逻辑功能时,只需要考虑实现上的正确性,而不需要考虑具体应用上的细枝末节
,代码的正确性更容易保证;到了具体应用时,只需要考虑应用场景,不需要考虑逻辑功能实现,也更能把握应用场景的准确性。
静态内存模块主要功能是根据指定的(模块号,所需内存大小)从某个固定的区域划分内存,在运行过程中不会有申请释放操作。
如有一块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,抑制全局变量的使用冲动。这里感觉有必要再重复一次,如果在开发逻辑功能代码时,发现需要添加全局变量才能解决问题,则需要考虑下
代码的设计是否足够抽象,是否非加不可。- 一种典型C语言开发思维及其可能的问题
- <C语言>递归思维及其实现-----汉诺塔问题
- 删数问题 典型的思维问题
- 一个C语言典型的内存泄露问题
- 开发遇到的典型问题
- ListView可能出现的问题及其优化
- C语言练习 (典型递归问题)汉诺塔问题
- C语言,一种如此美丽的语言
- Tomcat 中 JSP 无法编译问题的一种可能
- 通过openOptionsMenu无法打开optionsMenu问题的一种解决可能
- C语言中可能忽略的“注释”
- C语言开发应该注意的问题
- C语言的int, float,double相互转化(从本质上理解可能的问题)
- C语言回文及其转化问题
- C语言:函数声明与定义的参数不一致问题,后果可能很严重哦!!!!!
- gcc编译c语言(访问数据库mysql)时可能遇到的问题
- 菜鸟在C语言编译,链接时可能遇到的两个问题
- C语言风格字符串在DES加密后可能存在的问题
- perl 5 智能匹配
- 几度风雨--AndEngine环境配置总结
- Unity不同平台生成中预处理的注意点
- 最新的名企待遇,要找好工作的看看吧
- android调用带soapheader的webservice
- 一种典型C语言开发思维及其可能的问题
- 使用cocos2d-x 写的蜘蛛闪避小游戏------从cocos2d移植过来的~有哪里缺陷的可以提噢。亲。
- Re-installation failed due to different application signatures.
- 通过Android Layout Editor无法查看布局文件预览
- 转发和重定向的区别
- 常用Java静态代码分析工具的分析与比较
- rm: cannot remove `/var/lib/dpkg/lock': Read-only file system 修复磁盘并成功启动
- 关于nginx源码分析
- WinCE中断体系结构