一个小程序的经验总结
#0 本程序递归生成树,树在分叉时可能二叉,也可能三叉...,但不超过16叉,所以果断用了数组。
最终完成大概600行,最初估计大半天完成,最终花了三天。
最大感受:一旦数据定型,程序基本结构也定型了 | 自顶向下,化解复杂度控制很有效.
#1 数据类型应用 typedef 实现灵活性
2 typedef char Element;
#2 运用常量实现一定程度的可配置性
2 const normalInt indexCount = 32;
3 const normalInt resultIdxCount = 4096;
4 const normalInt MAX_target = 32;
5 const normalInt MAX_Pool_Size = 4096;
6 const normalInt indexOver = -1;
7
8 const Element dataTableOver = -1;
#3 对于重新整理数组数据 eg. { 1 3 -2 -2 3 -2 -2 -2 4 5 -2 -1 } 剔除负数->{ 1 3 3 4 5 }
用了容易编码的方式(申请同尺寸数组然后来回交换),有时间的话应该尝试下in place processing的方法。
同样非递归方式画出树我也使用了同样的伎俩,以后换个方式做。
即便这段代码也可以通过传递参数,根据参数动态申请存储器来加以改善.
normalInt len=1;
for (normalInt m=1; index[m]!=indexOver; m++)//m<=index[0]
if (index[m] > -1){
idx[len] = index[m];
len++;
}
for (normalInt m=1; m<len; m++) index[m] = idx[m];
index[len] = indexOver;
#4 牢记:C只有一种参数传递方式,下面代码演示了怎么通过函数调用申请资源、释放资源的过程
2 *p = (int*)malloc( 4*sizeof(int) );
3
4 return 0;
5 }
6
7 int deallocResource(int **p){
8 free(*p);
9
10 return 0;
11 }
12
13 int printArrayElements(){
14 int * p = NULL;
15
16 allocResourse(&p);
17 for(int m=0; m<4; m++) printf("%4d", p[m]);
18 deallocResource(&p);
19
20 getchar();
21
22 return 0;
23 }
#5 使用指针应谨慎,写了下面这个函数我明白了间址指针的用途
2 if (*tree == NULL) return 0;
3
4 for(normalInt m=1; m<=(*tree)->pointerCounter; m++)
5 destroyTree(&((*tree)->p[m]));
6
7 free(*tree);
8 *tree = NULL;
9
10 return 0;
11 }
#6 使用assert
程序完成后,随着实验数据变更,由于大量中间数据(达到近40k条目)生成,总会发生数组越界,虽然一开始也考虑到了这些,但并没有采取防护措施,只是注释了可能出问题的地方。其实这是不够的,最简单的方法自然就是使用断言了。断言的使用并非越多越好,而是只在必要的地方使用,这需要对程序逻辑有清晰的了解。最终检查下来,只在三个地方添加了断言,再有问题发生,断言会自然提示。那么生产性质程序呢,还是应当改变算法或者将断言部分换成相应代码。
#7 熟悉调试器
这样可以实现快速定位问题位置。
#8 调整程序默认栈大小
一方面中间数据的问题,另一方面递归带来的开销,默认的1MB栈空间捉襟见肘,程序直接栈溢出,在配置项Linker中修改成16MB,暂时解决了问题。详情参阅windows via c/c++第16章。
下图为visual studio 2012截图.
#9 应用Code Analysis来改善代码
设置如上图左下角所示。
打开后重新编译程序会生成XML分析结果,还是挺有意思的。根据提示我成功挖出了另一个bug,该bug被两重凑巧因素掩盖(Intel处理器小端法&&数据处理顺序),此前虽然观察到了这个现象但没找到原因,在使用此功能后...晚上突然想到了原因。