电脑桌面
添加盘古文库-分享文档发现价值到电脑桌面
安装后可以在桌面快捷访问

代码优化范文

来源:漫步者作者:开心麻花2026-01-071

代码优化范文(精选8篇)

代码优化 第1篇

一、正确使用变量提高运行速度

1. 强制声明变量

在VBE编辑器中的菜单“工具-选项”对话框中“编辑器”选项卡中, 您应该始终保持“要求变量声明”复选框被选中, 这样将在模块代码顶部出现Option Explicit语句, 要求您在编写代码时对所有出现的变量均进行声明, 这样, 在使用变量时减少内存需求并提高性能。必须始终用特定的数据类型声明所有变量。如果不使用特定的数据类型声明变量, VBA会创建Variant类型的变量, 这将比任何其他数据类型要求更多的内存。应该将那些存储整型值的变量声明为Long类型, 而不是Integer类型。

2. 减少变量的作用范围并及时释放变量, 特别是对象变量, 在其使用完后及时释放

例如:

3. 用VBA数组而不是单元格区域来处理数据

即可以先将数据写入到某个数组, 然后用一个语句就可以将数组中的数据传递到单元格区域中。在创建已知元素的确定数组时, 使用Array函数对于节约空间和时间以及写出更具效率的代码是非常理想的。

例如:

Dim Names As Variant

Names=Array (“Fan”, “Yang”, “Wu”, “Shen”)

Range (“A1:D4”) =Names

此外, 应该尽量使用固定大小的数组。如果确实选择使用了动态数组, 应该避免数组每增加一个元素就改变一次数组的大小, 最好是每次增加一定数量的元素。

二、尽量使用VBA原有的属性、方法和Worksheet函数

由于Excel对象的属性、方法和事件多不胜数, 对于初学者来说, 可能对它们不能全部了解, 这就产生了初学者经常编写与Excel对象的属性、方法相同功能的VBA代码段, 而这些代码的运行效率又显然与Excel对象的属性、方法完成任务的速度相差很大。例如:可以用Range的属性CurrentRegion来返回Range对象, 该对象代表当前区 (当前区指以任意空白行及空白列的组合为边界的区域) , 而同样功能的VBA代码却需要数十行。因此, 编程前应可能多地了解Excel对象的属性、方法, 尽量避免编写一大段代码来解决原本使用Excel中的属性、方法只要一条语句来解决问题的情况出现。

最大限度使用EXCEL的Worksheet函数是提高程序运行速度的极有效的方法。例如:要求平均成绩, 在VBA中可使用如下一段代码:

完成同样的功能, 使用如下的函数就比上面的代码快得多:

AverageValue=Application.WorksheetFunction.Average (Worksheets (1) .Range (“D1:D500”) )

Application.WorksheetFunction.Average表示调用工作表计算平均值函数。其他函数如Count, Counta, Countif, Match和Lookup等, 都能代替相同功能的VBA程序代码, 提高程序的运行速度。

三、尽可能减少使用对象引用

Excel中的对象众多, 包括Application、Workbook、Worksheet、Ranges、Range、Cells、Cell等, 每一个Excel对象的属性、方法的调用都需要通过OLE接口的一个或多个调用, 这些OLE调用都是需要时间的, 减少对对象引用能加快VBA代码的运行。

1. 引用同一对象的多个属性时使用With语句

2. 多次使用同一个对象引用时使用对象变量

如果你发现一个对象引用被多次使用, 则你可以将此对象用Set设置为对象变量, 以减少对对象的访问。如:

3. 在循环中要尽量减少对对象的访问以节省资源

如果对单元格赋相同的值200, 则可以使用如下代码:

Set Myrange=Workbooks (1) .Sheets (1) .range (“D1:D50”)

Myrange.value=200

4. 减少对象的激活和选择

如果你是通过录制宏来学习和使用VBA的, 则你的VBA程序里一定充满了对象的激活和选择, 例如:Workbooks (XXX) .Activate、Sheets (XXX) .Select、Range (XXX) .Select等, 但事实上大多数情况下这些操作不是必需的。例如:

四、在VBA中关闭屏幕更新

关闭屏幕更新也是提高VBA程序运行速度的有效方法, 通常能缩短运行时间的2/3左右。关闭屏幕更新的代码如下:

Application.ScreenUpdate=False

在VBA程序运行结束时再将该值改回来:

Application.ScreenUpdate=True

当然, 代码优化可能不是绝对必要的, 这取决于所做的工作。如果编写一个快速且简短的或者是一次性使用且与速度、简洁要求无关的代码, 您就不需要优化代码。另一方面, 如果要处理一个带有很多数据、工作簿、工作表等大的工程, 优化代码提高应用程序运行速度就非常有必要了。编写程序时注意使用更优的代码将会使你的代码更简洁、运行更快速、并且容易为您自已和他人阅读和调试。同时, 由于您的代码简洁, 因而输入更快, 工作效率更高。

参考文献

[1]张强.EXCEL2007与VBA编程从入门到精通.

[2]郭兵译.Excel 2007 VBA与宏完全剖析.

浅谈SEO网页代码优化 第2篇

下面是个人的一些见解,希望能对方大家有所帮助。

网页代码优化的原因:

(1) 可以减少网页的体积,加快网页的下载速度;

(2) 提高蜘蛛对信息的抓取的速度和准确性

(3) 有利于减少错误的代码,提高页面的对蜘蛛的友好性

(4) 便于管理人员维护,提高工作效率

(5) 减少网页的噪音,突出页面的主题

那我们怎样对网页代码进行优化?

(1)对网页中存在多余的代码,我们要学会清除垃圾代码,

网页中一般存在的垃圾代码会有空格符、空标签块、多余的嵌套标签、不必要的注释等。如:在每行的开始或结尾都存在着大量的空格符,我们需要把这些冗余代码删除,还有空白行。清除空白行有一种简单快捷的方法:可以将代码转至html代码编辑的模式按ctrl+A全选代码使用组合键shift+Tab删除。

(2)采用div+css排版。Div+css样式可以精简很多样式,将设计网页模版的部分脱离出来,放在一个独立的文件夹中,对于网站的维护和更改都相当的方便。且可以提高页面的浏览速度,增加客户访问的友好性。在css进行布局时,要有良好的命名习惯,适当的代码注释。

(3)引用js文件,并不是页面的特效越多越好(先css后js)。

(4)对于图片的优化,网站的图片不是越多越好,但要有图片,要完善图片的ALT属性,最好使用带有目标关键词的描述。

(5)对于错误的代码,我们可以利用3w工具进行检查。

对于页面标签的优化,H1定义为正文标题,即一级标题,它具有唯一性。H2为二级标题,主要出现在正文的文章标题上。H3为三级标题,一般出现在页面的侧边栏,H4-H6一般出现较少。值得注意的是在页面中和的区别,对关键字起到强调作用,而仅仅是加粗的作用。

函数优化的遗传算法代码实现 第3篇

遗传算法 (Genetic Algorithm) 是一类借鉴生物界的进化规律 (适者生存、优胜劣汰遗传机制) 演化而来的随机化搜索方法。它由美国的J.Holland教授于1975年首先提出, 其主要特点是:直接对结构对象进行操作, 不存在求导和函数连续性的限定;具有内在的隐并行性和更好的全局寻优能力;采用概率化的寻优方法, 能自动获取和指导优化的搜索空间, 并自适应地调整搜索方向, 不需要确定的规则。遗传算法的这些性质已被人们广泛地应用于组合优化、机器学习、信号处理、自适应控制和人工生命等领域。它是现代有关智能计算中的关键技术。

1 遗传算法实现步骤

遗传算法是从代表问题可能潜在的解集的一个种群 (population) 开始的, 而一个种群则由经过基因 (gene) 编码的一定数目的个体 (individual) 组成。每个个体实际上是染色体 (chromosome) 带有特征的实体。染色体作为遗传物质的主要载体, 即多个基因的集合, 其内部表现 (即基因型) 是某种基因组合, 它决定了个体形状的外部表现, 如黑头发的特征是由染色体中控制这一特征的某种基因组合决定的。因此, 在一开始需要实现从表现型到基因型的映射即编码工作。由于仿照基因编码的工作很复杂, 我们往往进行简化, 如二进制编码, 初代种群产生之后, 按照适者生存和优胜劣汰的原理, 逐代 (generation) 演化产生出越来越好的近似解, 在每一代, 根据问题域中个体的适应度 (goodness) 大小选择 (selection) 个体, 并借助于自然遗传学的遗传算子 (genetic operators) 进行组合交叉 (crossover) 和变异 (mutation) , 产生出代表新的解集的种群。这个过程将导致种群像自然进化一样, 后生代种群比前代更能适应环境, 末代种群中的最优个体经过解码 (decoding) , 可以作为问题近似最优解。

遗传算法的基本运算过程如下:

(1) 初始化:设置进化代数计数器t=0, 设置最大进化代数T, 随机生成M个个体作为初始群体P (0) 。

(2) 个体评价:计算群体P (t) 中各个体的适应度。

(3) 选择运算:将选择算子作用于群体。选择的目的是把优化的个体直接遗传到下一代或通过配对交叉产生新的个体再遗传到下一代。选择操作是建立在群体中个体的适应度评估基础之上的。

(4) 交叉运算:将交叉算子作用于群体。所谓交叉是指把两个父代个体的部分结构加以替换重组而生成新个体的操作。遗传算法中起核心作用的就是交叉算子。

(5) 变异运算:将变异算子作用于群体。即是对群体中的个体串的某些基因座上的基因值作变动。

群体P (t) 经过选择、交叉、变异运算之后得到下一代群体P (t +1) 。

(6) 终止条件判断:若t=T, 则以进化过程中所得到的具有最大适应度个体作为最优解输出, 终止计算。

2 遗传算法的Matlab代码实现

下面将分别介绍算法的每个过程的编程思路, 并分别给出相应的函数代码, 然后给出整个程序的主函数。接下来分别介绍这些函数的编写过程。

2.1 个体编码及初始种群设定

编码是应用遗传算法时要解决的首要问题, 也是设计遗传算法时的一个关键步骤。遗传算法的特点之一就是不直接对问题的实际决策变量直接进行操作, 而是根据问题的可行解对遗传个体进行编码, 然后对编码进行遗传运算, 以达到优化的目的。实际的遗传算法常用的编码有二进制编码、格雷码编码、实数编码等, 其中二进制编码简单易行、易于实现、便于利用模式定义对算法进行理论分析。因此, 我们的算法使用二进制编码。

进行二进制编码, 就是要对种群中的所有个体给出一个二进制码。因此, 需要根据问题的规模确定所要编码二进制的位数, 这需要3个变量, 即实际问题的上界max, 下界min和问题的精度precision, 根据这3个值即可求出所需要的二进制位数。同时还需要从所有的编码中随机地挑选一定数量的个体作为初始种群。实现的函数encodebit如下:

function [binarygeneration, bitnums] = encodebit (min, max, precision, popnum)

bitnums = ceil (log2 ( (max - min) ./precision) ) ;%求出所需要的二进制位数

binarygeneration = randint (popnum, sum (bitnums) ) ;%生成popnum行sum (bitnums) 列的数据

2.2 二进制的解码

经过二级制编码后, 构成的种群binarygeneration为二进制编码的集合, 为了在后续的复制选择过程中有效地计算个体的适应度, 必须将相应的二进制转换为对应的十进制数, 该过程称为解码。其中最关键的步骤是将相应个体的二进制转化为对应的十进制, 方式是想将该二进制个体转化为对应的十进制数dec, 然后利用dec*precisiondec+min求解得到。

2.3 进入下一代的候选个体选择

遗传算法在搜索中, 一方面要选择适应能力强的优良个体, 是搜索能收敛于全局的最优解;另一方面, 在搜索过程中要充分保持个体的多样性, 是搜索空间不断向新个体延伸, 避免收敛于局部最优解。我们采用适应度比例选择法和最佳个体保留方法来将现染色体代中的个体选择进入下一阶段。适应度比例选择法的原则是根据个体适应度的大小进行选择。适应度高的个体被选择的可能性较大, 个体被选中的可能性 (即概率) 是该个体适应度占全体个体适应度的百分比。该适应度比例选择法的原理是轮盘选择原理, 可供参考。所谓最佳个体保留方法是将适应度比较选择法中适应度最高的个体直接选入下一代中, 我们的方法是将适应度最高的个体替换适应度最大的个体。

function [evolvinggeneration, bestindiv, maxgoodness] = selection (oldgeneration, goodness)

popnum = length (goodness) ;

[maxgoodness, index1] = max (goodness) ;%找到适应度最大个体的索引值

[~, index2] = min (goodness) ; %找到适应度最小个体的索引值

bestindiv = oldgeneration (index1, :) ;

index = 1 : popnum;

index (index1) = 0;

index (index2) = 0;

index = nonzeros (index) ;%去除掉适应度最大和最小的个体

evolvinggeneration = oldgeneration (index, :) ;

evogoodness = goodness (index, :) ;

evopopnum = popnum - 2;%剩下进行的轮盘选择的个体个数

divp = evogoodness/sum (evogoodness) ;

pscum = cumsum (divp) ;

r = rand (1, evopopnum) ;

selectedindivs = sum (pscum*ones (1, evopopnum)

evolvinggeneration = evolvinggeneration (selectedindivs, :) ;%轮盘选择后的个体种群

evolvinggeneration = [evolvinggeneration; bestindiv; bestindiv];%将先前保留的最佳个体合并

2.4 个体交叉

交叉是遗传算法产生新个体的主要手段。两个染色体之间通过交叉重新组成新的染色体。交叉就是在染色体中随机地选择基因座, 将两个体中对应的基因座上的基因进行互换。选择基因座 (即交叉点) 有一点交换、k点交换、k点杂乱交换、均匀交换等。本文利用一点交换的方法来实现基因座上基因的交换。方法是先随机地选择基因进行配对, 这样就生成了现有的染色体种群数量一半的配对, 然后在配对的基因中随机地选择基因座, 将两个染色体从基因座开始直到尾部按照给定的概率crosspab进行交换。实现的函数如下:

2.5 个体变异

遗传算法引入变异的目的, 一是使算法具有局部搜索能力, 二是保持种群个体的多样性。当遗传算法通过选择交叉等操作已经接近最优解时, 变异算子局部搜索能力可以使解更加向最优解收敛。一般情况下, 变异概率应该取较小值, 否则可能是已经接近最优的解以较大的概率发生偏离, 从而导致局部搜索能力失效。因此在遗传算法中, 交叉操作可以认为是全局搜索操作, 而变异操作则作为补充的辅助操作。遗传算法通过两种操作具备兼顾全局和局部的均衡搜索能力。

下面的实现代码中, mutatepab是变异概率, 即将选定的基因座上的基因以概率mutatepab进行0-1转换。

function newgeneration = mutation (oldgeneration, mutatepab)

mpoints = find (rand (size (oldgeneration) )

newgeneration = oldgeneration;

newgeneration (mpoints) = 1-oldgeneration (mpoints) ;

3 函数全局最优解搜索实例

从encode和decode两个程序可以看出, 该程序已经考虑了多个参数编码的问题。为了测试程序搜索最优解的效果, 用一个单变量函数为例来验证该遗传算法程序的全局寻优能力。设函数为:y = cos (5*x) -sin (3*x) +10 , x∈, 函数的图形如图1所示。从图中可以看出函数的最优值接近12, 其横坐标大致在x=3.7附近。

初始种群大小popnum=20, 搜索精度precision=0.000 1, 交叉概率crosspab=0.6, 变异概率mutatepab=0.1。主程序如下:

为了观察程序的运行效果, 我们将演示不同进化代数的结果呈现如表1所示。

从表1中得出, 遗传算法所得到最优解和真实最优解非常接近, 并且收敛速度非常快。

4 结语

文中用Matlab实现了一个用于函数优化的单点交叉的遗传算法程序, 并给出完整代码, 程序在Matlab 7.11.0中调试通过, 该代码无需做任何改动即可以直接在计算机上运行。并且通过一个单变量函数的最优解搜索实例进行了验证。

参考文献

[1]刘国华, 包宏, 李文超.用MATLAB实现遗传算法程序[J].计算机应用研究, 2001 (8) .

[2]刘勇, 康立山, 陈毓屏.非数值并行算法——遗传算法[M].第二册.北京:科学出版社, 1997.

[3]席裕庚, 柴天佑, 恽为民.遗传算法综述[J].控制理论与应用, 1996 (6) .

代码优化 第4篇

在.NET中,内存管理是自动进行的。内存的再收集属于后台任务,这使开发者们更专注于手边的工作。在这篇文章中,我们来看一看为开发者提供帮助并且控制垃圾收集的接口。

处理设计模式(Dispose design pattern): IDisposable, Dispose, Finalize

共同语言运行时(CLR)无法清理数据库连接、窗口句柄、文件句柄一类的资源。因此,为开发者提供清理这类难管理的资源的机制是很有必要的。清理工作可以在Finalize方法中实现,Finalize方法在C#语言中被实现为垃圾破坏者。该方法的调用在垃圾收集器的控制下进行。

通常,你会需要一个确定的方法来清理像文件句柄一类难管理的资源。例如,现在你打开了一个文件进行写操作,当你结束将文件内容载入缓冲器后,你可能需要明确的关闭文件句柄。对于这类显式清理,.NET提供了处理设计模式(dispose design pattern)

需要进行显示清理的对象需实现IDisposable接口。IDisposable接口提供了Dispose方法,该方法不像Finalize方法,它不在开发者的控制之下。

既然Dispose方法的调用是显示清理,使用垃圾收集器收集这些对象没有必要。因此Dispose方法应该包含一个对GC.SuppressFinalize的调用,提示垃圾收集器在这个对象上不需要使用Finalize方法。

推荐在一个需要进行显示清理的对象上同时实现Finalize和Dispose方法。Finalize方法在Dispose方法没有被调用时提供一个备份的机制。垃圾收集器将执行对象的终结和防止难管理的资源的永久性泄漏。

在Listing A中的代码片断更清晰的描述了这些概念。

在Listing A中,SampleClass类使用了一个文件句柄,这是一个难管理的资源。因此,应为该对象实现IDisposable接口和提供Finalize方法,

用来清理文件句柄的代码是Dispose方法的一部分。GC.SuppressFinalize()在难管理资源被清除时也将被调用。

该类同时提供了垃圾破坏器(Finalize方法),也包含了用于清理难管理资源(文件句柄)的代码。

弱引用

.NET框架提供了另一有趣的特色,被用于实现多样的高速缓存。在.NET中弱引用通过System.WeakReference类实现。弱引用为引用的对象提供一项机制,使被引用的对象能够被垃圾收集器作用。ASP.NET高速缓存就使用了弱引用。如果内存使用率太高,高速缓存将被清除。

强制垃圾收集

.NET框架为开发者提供System.GC类来控制垃圾收集器的一些方面。垃圾收集可以通过调用GC.Collect方法强制执行。通常建议不要手动的调用垃圾收集器,而将其设置为自动方式。在某些情况下,开发者会发现强制垃圾收集将推进性能。但是,使用这个方法需要非常小心,因为在垃圾收集器运行时将延缓当前执行的线程。GC.Collect方法不应位于可能被经常调用的地方。这样做将使应用程序的性能降级。

.NET中的服务器和工作站版本

.NET构架包括相同的CLR的两种版本,分别对应于特定的目的。将他们分类为服务器运行时和工作站运行时,并分别在mscorsvr.dll和mscorwks.dll中实现。服务器版的CLR利用多处理技术使垃圾收集可以并行的完成。在单处理器环境使用工作站版本而不能使用服务器版本。

同样,对于垃圾收集器有高级设置项:concurrent garbage collection和nonconcurrent garbage collection。nonconcurrent设置用于服务器环境,这时应用程序无需做出响应。Concurrent设置使用在客户端环境中,这时用户界面被显示且应用程序需做出响应

Microsoft在Visual Studio.NET项目模块上创建了一些缺省设置,ASP.NET应用程序能够利用多处理器与装载CLR的服务器版本。但是,由于Windows客户端通常是胖客户端,它将装载CLR的工作站版本。

可以使用CorBindToRuntimeExAPI来覆盖这些设置和控制在多处理器环境中将加载哪种版本的CLR

工作效率

在两种版本的CLR中配合使用处理设计模型,开发者可以清理难于管理的资源。.NET框架提供面向垃圾收集的构架,将开发者从内存整理工作中解脱出来。这时,开发者只需要管理他使用过某些资源,因此使得工作变得简易同时提高了工作效率。

代码优化 第5篇

通过表单来收集用户信息,或者实现与用户的交互广泛存在于动态网站和其它B/S结构的系统中,虽然大部分用户能够认真填写表单,但不可避免会出现填写错误,通常为了解决此问题,开发人员会为表单设计验证功能,即在表单数据提交到服务器之前或者服务器之后对用户填写的数据进行验证,若遇到错误的填写则返回,要求用户进行更正。

在网站设计中经常使用Java Script验证客户端用户的输入,但很多时候验证过程大同小异,同时,如果表单中的表单域数目比较多,则导致对表单域的输入值进行动态插入处理的工作量大且烦琐。

本文提出利用JS文件优化网站表单验证和提交代码,提高网站开发的效率,使开发人员从复杂的表单验证和提交代码设计中解放出来,集中精力于网页的设计和功能的改进。

2 常规设计

2.1 验证表单域输入值的处理

表单的验证过程中需要判断用户输入的数据是否合法,如果不合法,则提示用户数据不合法,要求用户进行更正。

归纳起来可分为下面三个步骤:

(1)取得用户输入的数据;

(2)验证用户输入数据的合法性;

(3)不合法则返回有误的提示。

用户触发事件后,则调用相应代码执行上述步骤。

下面以具体实例详细说明表单域的验证过程,假设有一个收集用户信息的表单,要求用户输入中文姓名、生日和电子邮箱地址。设计Html表单程序如下:

表单设计完成之后,在页面中设计代码分别验证表单域各输入值的合法性。

用户提交表单之后,调用“Check Form()”函数针对每一个表单域的输入值进行验证,如果某一表单域的输入值不合法,则给出相应的提示。

2.2 表单提交数据的处理

在Web程序设计中,要处理表单提交的数据,通常要动态网站设计语言,这里以PHP语言为例来说明。

首先,在处理表单提交数据页面do.php中设计代码,提取表单提交的数据。

取得数据后,然后再设计插入数据库的SQL语句如下:

3 优化设计

3.1 设计表单内容

首先为表单中的元素定义如下几个属性:

data Type:表示该表单域的类型;

msg:当该表单不合法时的提示信息;

在每一个需要输入数据的表单域的“name”和“id”属性值加“t_”,主要程序中区别需要输入数据的表单域和其他表单域;

增加一个表单域insert Msg主要是为了表单提交后由系统自动生成完整的SQL插入语句。

3.2 创建JS文件

创建一个JS文件,取名为validate.js。validate.js文件具体内容如下:

(1)表单验证处理代码

当某个表单的值没办法用简单的正则表达式来验证时,可以为其单独定义一个验证函数实现复杂的验证过程。

(2)表单提交处理代码

3.3 引用JS文件

在网页html文件

之间插入引用“validate.js”文件代码如下:

其中,“src=validate.js”指定具体引用的JS文件,可以使用相对路径,也可以使用绝对路径。

3.4 验证实现过程

利用JS文件中的“Check Form()”函数可以方便且统一地对网页中所有需要验证的表单域输入值进行验证,以确保每个表单域都包含有效值。若发现某个表单域缺少有效的数据,则会给出错误提示信息并返回,否则继续执行。此过程如图1所示。

引用JS文件中的“Insert Form()”函数,可以将表单中的所有提交的表单域及其取值联系起来,最终返回用户一个完整的SQL插入脚本,不需要程序开发人员再自己设计。

上例提交表单后,如果输入的值都合法,则最终会返回:

4 结语

利用JS文件优化表单验证和提交代码,可以灵活且统一地对网页中的表单域输入值进行验证,以确保每个表单域都包含有效值;另由系统自动生成SQL插入脚本,减少开发人员的工作量,大大提高了效率。在实际项目的开发中,表单处理很重要,利用JS文件对它处理优化,对于网站的设计和开发有很好的借鉴作用。

摘要:针对网站开发中设计表单验证和提交代码烦琐的现象,通过对JavaScript脚本语言的深入研究,本文设计并实现了通过JS文件来优化网站表单验证和提交代码,从而提高了代码的可重用性、简洁性、可读性和扩展性,提高了网站的开发效率。

关键词:JavaScript,表单域,验证,提交,优化

参考文献

[1]卢云鹅等.JavaScript深度剖析(第2版).北京:北京大学出版社,2004.

代码优化 第6篇

本文总结了作者使用Visual Basic积累的一些经验, 从不同角度谈谈Visual Basic应用程序代码的优化问题。

1. 应用程序代码优化的概念

一般对Visual Basic应用程序的优化目地为提高速度和减少占用空间。在作应用程序优化之前, 应清楚优化的目的是什么, 这是确定优化策略的关键。一旦明确了优化的目的, 就可以进一步确定优化的对象和优化方法, 即采用什么优化方法、在什么地方优化以及优化到什么程度等问题。

2. 空间优化

在设计Visual Basic应用程序时, 经常会受到可用内存和系统资源的限制。许多情况下, 缩小应用程序的大小是很必要的。小的应用程序加载快, 而且, 由于占用内存少, 还可同时运行其它程序。通过优化应用程序所占的内存可提高程序性能。通过以下几种方法可以减少程序大小, 完成空间优化。

2.1. 减少加载窗体、控件数目和用标签代替文本框

每一个加载的窗体, 无论可视与否, 都要占据一定数量的内存 (其数量随窗体上控件的类型和数量, 以及窗体上位图的大小等的不同而变化) 。只在需要显示时才加载窗体, 不再需要时卸载窗体 (而不是隐藏窗体) 。记住, 任何对窗体的属性、方法或控件的引用, 或对用New声明的窗体变量的引用, 都会导致Visual Basic加载该窗体。

使用Unload方法卸载窗体时, 只能释放部分窗体所占空间。要释放所有空间, 可用关键字Nothing使窗体的引用无效:Set Form=Nothing

当设计应用程序时, 窗体应尽量少用控件。实际的限制取决于控件的类型和系统, 但实际上, 含有大量控件的窗体将运行缓慢。一项与之相关的技术是:设计时尽可能地使用控件数组, 而不是在窗体上放置大量同类型的控件。控件数组是一组具有共同名称和类型的控件。它们的事件过程也相同。

2.2 使用磁盘文件或资源和组织模块

在设计时, 直接放入应用程序的数据 (像属性或代码中的文字字符串和数值) 将增加运行时应用程序占用的内存。运行时从磁盘文件或资源中加载数据可减少占用内存。这对大位图和字符串特别有价值。资源文件实际上是由一系列独立的字符串、位图或者其他项目组成的, 其中每一项都有一个唯一的标识符。可以使用类似于在Microsoft Visual C++中提供的文本编辑器和资源编译器创建资源文件。编译过的资源文件带有.res扩展名。

Visual Basic只在需要时才加载模块即当代码调用模块中的一个过程时, 模块才被加载到内存。如果从未调用一特定模块中的过程, Visual Basic决不加载该模块。因此, 尽量把相关的过程放在同一模块中, 让Visual Basic只在需要时才加载模块。

2.3 考虑替换Variant数据类型

Variant数据类型使用极其灵活, 但是比其他数据类型所占内存大。当要压缩应用程序多余的空间时, 应考虑用其他数据类型替代Variant变量, 特别是替代Variant变量数组。

每一个Variant占用16个字节, 而Integer占2个字节, Double占8个字节。变长字符串变量占用4个字节加上字符串中每一个字符占用1个字节, 但是, 每一个包含字符串的Variant都要占用16个字节加上字符串中每一个字符占用1个字节。因为它们太大, 因此在用作局部变量或过程的参数时, Variant变量是特别烦人的, 这是因为它们消耗堆栈空间太快。但在有些情况下, 使用其他数据类型替代Variant, 灵活性降低了, 为弥补损失的灵活性, 不得不增加更多的代码。结果是大小没有真正的减小。

2.4 使用动态数组并在删除时回收内存

使用动态数组代替固定数组。当不再需要动态数组的数据时, 用Erase或ReDim Preserve放弃不需要的数据, 并回收数组所用内存。例如, 用以下代码可回收动态数组所用空间:Erase My Array, 这里, Erase完全删除数组;ReDim Preserve则只缩短数组而不丢失其内容:Re Dim Preserve My Array (10, smallernum) 。删除了固定大小数组, 也不能回收该数组所占空间--只是简单地清除数组每一元素中的值。如果元素是字符串, 或包含字符串或数组的Variant变量, 那么删除数组可回收这些字符串或Variants所占内存, 而不是数组本身所占内存。

3. 速度的优化

3.1 使用整数 (Integer) 和长整数 (Long)

使用整数 (Integer) 和长整数 (Long) 提高代码运行速度最简单的方法是使用正确的数据类型.在大多数情况下, 可以将Single, Double和Currency类型的变量替换为Integer或Long类型的变量, 因为Visual Basic处理Integer和Long的能力远远高于处理其它几种数据类型 (长整形是Visual Basic中最快的变量类型, 最慢的数据类型是货币或者Variant类型) 。在大多数情况下, 选择使用Single或Double的原因是因为它们能够保存小数。但是小数也可以保存在Integer类型的变量中.例如程序中约定有3位小数, 那么只需要将保存在Integer变量中的数值除以1000就可以得到结果。使用Integer和Long替代Single, Double和Currency后, 代码运行速度可以提高将近10倍。

3.2 避免使用Variant变量

Variant变量非常灵活, 但它也是最臃肿、最无效的数据类型。每个Variant变量使用16字节, 还要加上实际的数据, 而每个整型变量只需2个字节, 因此, Variant变量不仅消耗内存, 而且还消耗栈空间。通常使用Variant变量的目的是为了减少设计的工作量和代码量, 也有的是为了方便而使用它。但是如果程序经过了严格设计和规范编码, 完全可以避免使用Variant变量。

3.3 将常用的属性存在变量中

变量的访问和设置速度比属性操作快。如要经常用到某一属性的值 (若在循环体中) , 则可以在循环体外把该属性值赋给某一变量, 以后用该变量替代该属性值, 这样能提高代码的执行速度。一般来说, 变量的处理速度比同类型的属性处理速度快10到20倍。同样的技术可用于处理函数的返回值。将函数的返回值存于一变量, 则可避免经常调用动态连接库 (DLL) 及Mdvbvm60.DLL。

3.4 使用内嵌过程替代过程调用

采用过程调用使代码更具有模块化的风格, 但模块调用会增加额外的操作和处理时间。若循环体内多次调用某一过程, 就可以直接把该过程写到循环体中去, 以消除过程调用时的额外开销。但这样却又增加了应用程序的大小, 同时, 也增加了调试的难度。

4. 结束语

在设计和编写应用程序时, 可以采用不同的方法来优化应用程序的性能。有的能加快应用程序的运行, 有的能缩小应用程序的大小。本文讨论的是VB应用程序中各种有利于代码大小的优化技术。在优化时, 我们应该有两方面的认识:首先, 应用程序的优化必须在开发时就实行优化, 而不是在应用程序开发周期的最后阶段 (即编译时) 才进行优化。其次, 优化并不是在所有的情况下都是有益的, 所以选择优化的具体类型应赖于应用程序的性质及其目的

摘要:本文首先讨论了程序代码优化的重要性和必要性, 并对优化的一些概念作了简单说明, 接着以Visual Basic应用程序代码优化为利, 提出了可以在在空间和时间两个方面对其效率进行优化, 最后比较详细的介绍了如何减少程序代码大小和提高程序运行速度的几种常见方法, 通过这些方法可以减少系统资源利用和提高运行速度以达到程序的高效率。

关键词:代码优化,Visual Basic,空间优化,时间优化

参考文献

代码优化 第7篇

如果一个软件的源程序中的一个代码段和同一程序中的另一个代码段在结构或语义上类似,这些代码段就成了代码克隆。 代码克隆的存在增加了软件维护的困难。如果所修改的部分有克隆的代码段,这些克隆代码段也常常需要修改。这一情形有三个主要缺点:第一,增加了编辑修改的工作量,因为软件开发者要修改所有的克隆代码;第二,因为修改不是同时进行,难以确保修改的一致性;第三,要靠人力找出所有的克隆不是一件容易的事,难免会漏掉一些克隆代码。如何检测出源代码中存在的克隆代码因而成了软件工程研究中的一个重要课题。

1 代码克隆问题

1.1 代码克隆的定义

基于对类似度的定义的不同,代码克隆也有不同的定义。 最严格的克隆定义要求两个代码段之间完全相同,即一段代码是另一段代码的逐字重复。这样的定义很严格,没有考虑代码格式的不同,或系统性的变量名置换等引起的词法上的表面区别。以图1中的两个程序A和B为例,按照完全克隆的定义,只有A的行1和B的行2才是一对代码克隆。这样严格的定义并没有什么实际应用价值。更一般的代码克隆的定义不考虑程序段之间由于格式或变量名的不同而引起的词法上的区别,而着重于程序段之间的结构和语义上的类似。按照这一定义,图1的程序A和B有两个代码克隆对:A的行1至行4与B的行2至行4;A的行5至行7与B的行6至行7。

进一步观察上面的两个代码克隆对,可以发现这两个代码克隆对之间只被程序B的行5隔开。如果允许克隆对中有少量不同的语句的存在,上面的两个克隆对就可以合并成一个克隆对,即A的行1至行7与B的行2至行7构成一个克隆对。这样当中含少量不同语句的克隆对一般被称为断层克隆。考虑到程序员在编程过程中采用cut/paste后通常会进行修改,增加或删除一些语句,检测断层克隆就变得很有必要。

1.2 代码克隆现象和原因

代码克隆在软件系统中很普遍。即使是业界公认的高质量系统如X Windows System仍有19%的代码克隆存在[1],Linux的核心部分有15-25%左右的代码克隆[2]。一般软件系统中的代码克隆的比例更高,有些甚至高达38%[1]。

软件开发者常用的copy/paste是造成代码克隆的主要原因之一。尽管几乎所有的软件工程教科书都反对copy/paste,要求软件开发者把相类似的操作抽象成宏代码或函数,避免代码克隆的存在,但是对那些系统中存在的代码克隆的分析发现,有些代码克隆的存在是不可避免的。其中的第一个原因是对系统效率的考虑,因为函数调用会带来额外的系统负担,在很多情况下,软件开发人员在权衡利弊后会选择克隆代码。其次,有些系列操作在问题领域中没有对应的基本的概念,难以抽象成一个意义的函数。如果勉强把它们抽象成一个函数,会大大增加程序理解的难度。比如说,调用库函数时往往需要一些固定的操作组合等等。第三,象各种驱动程序等软件模块在结构上大致相同, 但具体细节多有不同,难以抽象为函数进行系统性的复用。

2 克隆检测的一般过程

如图2所示,检测源代码中含有的克隆代码由三个主要步骤组成:中间形式生成,原型克隆检测和克隆报告生成。

源代码是为编译器以及软件维护人员而编写的,其中含有大量与克隆代码检测无关的信息。克隆代码检测一般无法对源代码直接进行操作,必须要从源代码中抽出只与克隆代码检测有关的信息,生成便于进行克隆检测的中间形式。

原型克隆检测对源程序的中间形式进行分析,检测出具有相同的中间形式的程序段。由于这一过程的结果只是在中间形式上的相同,而不一定是最后所要求的代码克隆,我们称之为原型克隆。通过对这些原型克隆的分析和剪接处理,克隆报告生成把原型克隆还原成有意义的源代码克隆对,输出具有良好可读性的克隆报告。把原型克隆还原成源代码的克隆需要中间形式和源代码之间的对应关系,因此,中间形式生成过程还需要生成一个程序格式信息表格,用于纪录中间形式和源代码之间的对应关系。

克隆代码检测方法取决于采用什么样的中间形式来表示程序源代码。抽象语法树和Token序列是两种主要的中间形式,本文将在下面两节里讨论基于这两种中间形式的检测方法。

3 基于抽象语法树的方法

基于抽象语法树(AST)的方法利用源程序的AST作为代码克隆检测的中间形式。它首先对源程序作句法解析,生成抽象语法树,然后把每一棵子树与其他的子树进行比较,找出相同的子树从而找出克隆的代码段。对有N个结点的AST,这一子树逐一比较过程需要O(N^3)的计算时间,由于一行程序语句平均会生成10个抽象句法树结点,实际上的处理时间是O(L^4), L为程序的语句数。这样的方法显然无法处理大型的程序,因此本方法的关键在于提高子树的比较效率,主要有直接散列法和特征矢量法两种方法。

3.1 直接散列法

直接散列法利用散列函数把每一个树结点映射到散列值,然后根据散列值对子树进行比较。AST中的仅含有标识符的叶子结点都被映射到同一散列值,以消除仅仅由标识符而引入的区别。

子树T1和T2的相似性可用下列公式进行计算:

Sim(T1, T2) = 2 * S / ( 2 * S + T1-only + T2-only)其中S表示T1和T2所含有的结点中具有相同散列值的结点的个数,T1-only表示仅在T1中出现的结点的个数,T2-only则表示仅在T2中出现的结点的个数。一旦两个子树的相似性超过一个预定的阈值,就可以把它们当作代码克隆。克隆检测的精确度可通过对阈值的设定来调节,如果设定的阈值为1.0,那么只有那些仅在标识符上有所不同的代码段才会被判断成克隆代码。

3.2 特征矢量法

特征矢量法用多维矢量表示AST结点的结构信息,每一维代表程序语言的基本成分。文献[4]提议用下列9维特征来表示C和Java源代码:

[标识符,常量,赋值,增1,数组,条件,表达式,宣言,循环]

每一个结点的特征矢量值通过后序遍历计算,父结点的值是所有子结点的值和父结点本身的特征值的矢量和。图3例示赋值语句x=1的特征矢量值及其计算方式,该语句的特征矢量值是[1],因为在其AST中,它的父结点是赋值语句,其特征值是[0,0,1,0,0,0,0,0,0];它还有两个子结点,分别是标识符[1]和常量[0,1,0,0,0,0,0,0],把上面三个特征值相加即得到这一赋值语句的特征矢量。

这样一来,子树的类似性的比较就变成了特征矢量的比较。可以考虑简单地采用计算矢量间的欧几里德距离来计算子树的类似性。为了避免逐对比较所需的大量计算时间,可以用合适的散列函数把矢量映射到散列值。LSH(Locality Sensitive Hashing)[5]就是这样的一种散列法,它能把距离相近的矢量映射到同一散列值上,距离较远的矢量映射到不同的散列值上。

4 基于Token序列的方法

基于Token序列的克隆代码检测法对计算机源代码作简单的词法分析,把源代码转换成Token序列,通过检测Token序列中的重复序列找出克隆的代码。

4.1 Token序列生成

为提高克隆检测的效果,基于Token的检测方式会采取下列几个步骤对源代码作变形处理生成源代码的Token序列以消除源代码在表面上的区别。

最基本的变形处理是删除所有的对程序功能没有影响的注释部分和各种冗余的空白字符(如重复的空格和制表符等)。不同的方法对换行符的处理有些不同。比如说CCFinder[6]删除所有的换行符,把整个程序变成一行Token序列。删除换行符有利也有弊。它的优点在于可以最大程度地消除程序格式的不同,但它也会增加系统的虚报,即检测出实际上并不是克隆的代码。同时由于Token序列中不再有换行符,这一方法也会报出跨行的代码克隆,需要在后期处理的克隆报告生成中加以删减,把克隆代码的起始点和终止点移到语句行首与行尾。

第二类的变形处理删除程序中的冗余结构。这一类处理与程序设计语言的特性有关系。以Java程序为例,可以考虑删除import语句、package定义语句等;还可以删除else、break、const、protected、private、public、static等保留字。这些字符对要生成执行代码的编译程序有意义,但对基于Token的克隆检测没有影响。这一类变形处理的目的是缩短最后生成的Token序列。由于检测Token序列中的重复子序列的算法复杂度与Token的长度有关,缩短Token序列既可以提高计算的速度也降低对内存的要求,从而提高检测系统的计算效率和可扩展性。

第三类的变形处理把程序中的各种标识符用特殊的符号代替。标识符的代入有下列几种方式。第一种代入方式保留标识符的类型的区别,把同一类型的标识符转换成同一符号,不同类型的标识符转换成不同的符号。比如说所有的整数类型的标识符都用1表示,而所有的字符类型的标识符都用2表示;同样常数也按其类型转换成特殊符号;用户定义的函数名全都转换成同一个特殊符号。第二种代入方式比较彻底一些,用同一个特殊符号代替所有的变量名、函数名和常数。第三种代入方式最彻底,用同一个特殊符号代替程序中所有的(包括程序语言予定义的)变量名、函数名、类型名和常数;这种参量代入后产生的Token序列中除了运算符和程序语句结构里的保留字(如if, while)以外就只有特殊符号了。图1中的程序A经第三种彻底代入后就变成了下列的Token序列:

$=$;$=$[$];$=$[$];for($=$;$<$;$++) {if ($[$]>$)$=$[$];if($[$]<$)$=$[$];}

符号代入的彻底程度和克隆检测的效果有直接的关系,符号代入范围的扩大可减少漏报,但会同时增加虚报,相反低范围的符号代入会导致漏报的增加但也会减少虚报。根据检测工具的具体目的和用途,设计人员应采用相应范围的符号代入。

经过上述变形处理之后,程序源代码就变成了Token序列。

代码克隆检测问题也就和检测重复的子序列问题相同,可以利用各种字符子串的检测算法或数据挖掘中常用的频繁项集(frequent itemset)法。

4.2 后缀树检测

后缀树是一种为快速找出字符子串而设计的树结构[6],当用它来表示一个字符串S时,字符串S中的每一个后缀子串都有一条从根结点到叶子结点的路径。其叶子结点代表子串的结束,叶子结点的个数与字符串S中字符的个数相同;边代表一个共同的字符子串。图4是abcdabe的后缀树,它共有7个后缀分别为{1:e, 2:be, 3:abe, 4:dabe, 5:cdabe, 6:bcdabe, 7:abcdabe}, 其中后缀2和6有共同的子串b, 后缀3和7有共同的子串ab.

利用后缀树检测克隆代码时,我们首先把源程序生成的Token序列当作一个字符串,并构建出它的后缀树,然后从后缀树中抽出共同的子串[6]。

4.3 频繁项集挖掘

抽出频繁项集是数据挖掘中常用的方法。设定一组项的序列集合D={abcd, abce, bced, abcf, cd}, 其中3-项集abc的出现频率是3,即有3个支持度;2-项集cd的出现频率是2,其支持度为2。 D中所有满足支持度为2以上的频繁项集为{abd:3, ab: 3, bc: 4, cd:2, a:3, b:4, c:5, d:3}。频繁项集挖掘就是从给定的序列集合中抽出满足预定支持度的所有项集。各类的频繁项集挖掘法都可以用来检测代码克隆。

CP-Miner[7]尝试了使用闭合序列模式(closed sequential pattern mining)挖掘算法检测代码克隆。该算法的基本原则是从k-1项的频繁项集和1项的频繁项集中计算k项的频繁项集,如果k项集是频繁项集,那么它的所有子序列都应该是频繁项集。以上述D为例,为了计算3项频繁项集,它首先计算支持度不小于阈值(比如说2)的2项和1项频繁项集,分别标记为F2和F1, F2={ab, bc, cd}, F1={a, b, c, d, e}。然后它连接F2和F1得到 F′3={aba, abb, abc, abd, abe, bca, bcb, bcc, bcd, bce, cda, cdb, cdc, cdd, cde}, 再计算 F′3中的各个项集是否满足预定支持度,得到最后的3项频繁集F3。为提高计算速度,在具体算法中,该方法首先计算出F2中的每个项集t在D中存在的后缀集合Dt,比如说,Dbc={d, e, ed, f}, 在连接过程中,只有当Dbc中所有元素的首项的出现频率满足预定支持度,它才和bc连接,加入到F3中。在上例中,bc只和e连接, 因为Dbc中的三个首项d, e, f当中,只有e有两个支持度(e和ed)。所以F3中也只有bce,不包含bca, bcb, bcc, 和bcd。依次类推,计算出所有的频繁项集。

由于频繁项集挖掘是对一个序列集合进行的操作,和后缀树法不同,它要把源程序分割成一组序列。根据代码克隆检测的目的,可以把程序的Token序列按文件单位、函数单位、或语句段进行分割,得到一个Token序列集合。

4.4 Token序列的散列法

后缀树法和频繁项集法的检测速度和效率都和程序的Token序列长度有关。为进一步提高检测的速度,可以考虑采用散列法缩短最后生成的序列。在从源代码生成Token序列时,只删除那些跨赋值语句行的换行符,但保留其他的如循环和条件语句中的换行符。然后用一个散列函数把每一行的Token转换成一个散列值,因为相同的Token行都有相同的散列值,代码克隆检测就可以变成检测重复的散列值序列。如果把每一个散列值当作新的Token,上述的两种方法都同样可以适用。这个经过散列后生成的新的Token序列要比原来的序列短得多,一般只有5分之一左右,从而可提高近5倍的检测效率。

5 结论与展望

本文阐述了代码克隆的概念、存在原因、以及代码克隆的检测方法。由于代码克隆对软件的理解、维护和进化有及其重要的意义,是近年来软件工程界的一个热门研究课题。

目前对代码克隆的研究着重检测,其主要目的是在检测后尽量通过抽象、代码重构等手段消除软件系统中存在的克隆代码,以提高软件系统的质量和可维护性。但是从对造成代码克隆现象原因的分析可以知道,并不是所有的代码克隆都可以消除的。因此有些代码克隆可能会长期在系统中存在。为了克服代码克隆带来的维护上的困难,如何帮助软件开发人员更好地管理难以消除的克隆代码是另一个尚待研究的课题。

参考文献

[1]Baker B S.On finding duplication and near-duplication in large soft-ware systems.In Proceedings of the13th Working Conference on Re-verse Engineering,IEEE CS Press,1995:8695.

[2]Antoniol G,et al.Analyzing cloning evolution in the Linux kernel.Journal of Information and Software Technology,2002,44(13):755765.

[3]Baxter I D,et al.Clone detection using abstract syntax tree.In Proceed-ings of1998International Conference on Software Maintenance.1998:368377.

[4] Jiang L,et al.DECKARD: Scalable and accurate tree-based dection of code clones, in Proceedings of International Conference on Software Engineering,2007.

[5]Gionis A,Indyk P,Motwani R.Similarity search in high dimensions via hashing,in VLDB1999:518529.

[6]Kamiya T,Kusmoto S,Inoue K.CCFinder:A multilinguistic token-based code clone detection system for large scale source code.IEEE Transactions on Software Engineering,2002,28(6):654670.

代码优化 第8篇

关键词:代码安全检测,漏洞检测,代码审查,参考树

0 引言

软件漏洞是当今信息系统绝大多数安全漏洞的来源。软件漏洞是软件中存在的一些缺陷,这些缺陷可以被第三方或程序利用来进行未经授权的资源访问,或改变控制权限来执行其他操作。软件漏洞轻则造成相应的经济或财产损失(如敏感信息被盗,不可用的服务),重则导致严重的灾难,尤其是发生在被人类生活和生计所依赖的关键基础设施的软件系统上(如核,生物,化学实验室,电网,水处理和分配系统,空中交通控制和交通信号系统)。

与代码审计有关的前期工作[1,2,3,4,5]多聚焦于缓冲区溢出漏洞。D.Wagner等提出了一种用于发现C语言代码中潜在缓冲区溢出的关键安全性问题的方法[6]。该方法将缓冲区溢出检测转化为一个整数约束问题,并使用一些简单的图论技术构建了一个整数约束求解的有效算法。最后利用安全知识设计一个可以捕捉有可能会在实际程序中出现的安全相关性漏洞的启发式。Vinod Ganapathy等提出了一种将C字符的操作建模为一种线性规划,并根据线性规划的相关文献来确定缓冲区范围的方法[7],来了解并消除源代码中存在的缓冲区溢出漏洞。

1 源代码安全漏洞的检测过程

代码安全审查的对象是实践于应用中的源代码。这些审计的主要目的是发现安全漏洞和缺陷,然后准确地识别与修复。事实上,安全漏洞的概念被引入到软件主要是由于大多数软件及其功能无法在未知情况下保持正常运行。一般来说,攻击者有两个渠道来利用软件的缺陷:一是通过软件的输入;二是通过软件与其他应用层实例之间的接口。

几乎所有的可利用安全漏洞都可以被用于执行非预期的操作,特殊的输入、接口或环境入口会触发这些安全漏洞从而对系统造成破坏。例如,黑客总是构造一些精心设计的字符串作为应用程序的输入来使其崩溃,或改变它的控制流来做其他恶意操作(如拒绝服务攻击,SQL注入和跨站点脚本攻击)。可利用安全漏洞总是与外部输入、接口或其他环境入口相关,所以检测到它们最有效的方式是从外部对操作入口进行性跟踪(如本地输入、网络输入、临时文件或其他输入,接口或其他环境入口,等),通过此方法找到所有可利用安全漏洞。检测源代码安全漏洞过程如图1所示。

第一步,检测和定位源代码中所有的操作入口。这些操作入口包括但不限于用户输入,网络输入项,I/O输入项等。而可以用来检测这些操作入口的方法是找到所有用于获取外界输入、用于与接口和其他环境入口做交互的API(Application Programming Interface,应用程序编程接口)。

第二步,为每个在第一步中定位的操作入口建立一个参考树。每个引用或引申的参考项可以追溯到的节点应该包含在树中。记录和保持原有的安全属性对进一步操作参考树来说是十分必要的,其中安全属性指的是程式化安全规则(FSR)。FSR可以定义不同树的安全性能,它是一种可被机器识别的自动处理过程。FSR包括但不限于正则表达式,ACL,等。

第三步,对参考树的遍历,即遍历树的节点,利用安全属性来检查是否有可利用安全漏洞的存在。该安全漏洞检测方法可以是但不限于,检查父亲的FSR和孩子的FSR之间的矛盾等。

第四步,根据在第三步检测到的安全漏洞来产生告警提示,并提供基于第三步分析的修正建议。

最后,开发人员可以在建议的基础上修改其源代码,并再次执行此过程,以保证修正过程中不会引入任何新的漏洞。

2 一种高效检测源代码安全漏洞的代码审查方法

一种高效检测源代码安全漏洞的代码审查方法总共包括四个模块:操作入口定位器,解析引擎,漏洞检测器和告警修复模块,系统结构如图2所示。

(1)操作入口定位器

操作入口定位器可以实现(但不限于)通过搜索所有用于接收诸如scanf(),getchar(),getwindowstext,socket输入等外部输入的API(不同的程序语言可能有不同的API)。

(2)解析引擎

解析引擎利用的是编译器技术,它可以找到所有可以追溯到操作入口的代码块。它可以在句法分析中自动分配一些FSR规则。最后,该引擎将产生源代码参考树,如图3所示。

(3)漏洞检测器

漏洞检测器将根据源代码参考树进行处理。它会对参考树节点的FSR进行检查,分析是否有任何安全漏洞。

(4)告警修复

告警修复模块将提供安全漏洞的警告信息,以及修复被探测器探测到的安全漏洞的建议。分析模块将从根节点开始遍历参考树,并与每个节点的孩子节点比较FSR。当它检测到任何安全漏洞,分析模块会产生警告,并给出修复建议。用于实施该方法的伪代码如下:

3 应用

3.1 情景一:代码注入漏洞

代码注入指的是外部代码被直接插入到程序或脚本中被执行。一般可执行代码被注入前先插入到非可执行的文本串中,然后再提交给应用程序,

代码注入是通过在本应该不可执行的文本字符串中嵌入可执行代码引起的。有两种方法来定义的FSR:一个是定义的字符串的属性为非可执行;另一种方法是指出不应该包含在文本中的字符串,包括任何可执行的“关键字”,或表达式中的字符串(如脚本标签出现<script>alert(″OK″)</script>,或SQL请求中出现单引号(′)或双破折号(——))。在这里,选择了第二种方法,其可以指定FSR的格式为[FSR:限制规则]。

内容限制可以通过正则表达式来执行。例如,“^.$”可以表示所有可能的字符串,这可以表示所有外部原始输入。在将外部输入命令转化为SQL查询命令时,可以设定此内容限制,即不包含SQL特定字符,具体命令格式如下:

其中:

w*表示零个或多个字母或下划线字符;

%27是指无所不在的单引号或它的hex等值;

(%6F)|o|(%4F)((%72)|r|(%52))表示单词“或”以它的上和下壳体六角当量的各种组合。

图4所示的是通过解析引擎建立的参考树。所有节点都将被编号为n.n,这体现了父节点和子节点之间的关系。同时,该节点的FSR和位置信息也将被记录下来。安全性分析模块将通过检查每个子节点的FSR是否与其父节点一致来处理参考树。一种解决方案是从参考书中的叶节点开始遍历,比较它们与父节点的FSR。例如,在图3中,节点1.2.2是一个节点1.2的子节点,其FSR中的“的”意味着不应该包含任何SQL相关的特定字符,但它的父节点(节点1.2)的FSR为“”,这意味着它可能包含所有可能的字符,包括SQL相关的特定字符。所以在这里发现了一个安全漏洞,系统的告警和修复模块会产生警告,并产生诸如“节点1.2需添加字符串验证”这样的修复建议。

3.2 情景二:缓冲区溢出漏洞

一般来说,当一个数据块B写入缓冲器A时,若B的大小大于A的合法分配的大小就会发生缓冲区溢出。缓冲器溢出的另一种形式是整数溢出,这是程序的一种潜在问题,即程序中保存的数据大小是通过以字节为单位的数据类型限制的。在这里指定FSR的格式为{FSR:."^{length}$"}。

图5展示了它通过解析引擎建立的一棵参考树。所有节点都将被编号为n.n,这体现了父节点和子节点之间的关系。同时,该节点的FSR和位置信息也将被记录下来。安全性分析模块将通过检查每个子节点的FSR是否与其父节点一致来处理参考树。一种解决方案是从参考书中的叶节点开始遍历,比较它们与父节点的FSR。例如,在图6中,节点1.2有两个子节点,子节点1.2.1和1.2.2,他们允许的最大缓冲区长度为30和20。因为它们的父节点(节点1.2)的允许长度为50,所以当子节点到父节点之间可能会发生溢出。于是在这里发现了一个安全漏洞,系统的告警和修复模块会产生警告,并产生相关修复建议如“建议修改节点1.2的最大缓冲区长度为20”,如图6所示。

3.3 情景三:特权提升漏洞

特权提升漏洞允许攻击者或代码获取其本不应有的更高水平的权限。这里的特权可能不仅代表用户(如管理员,超级用户或访客)的特权,也代表代码(如内核或用户模式)的特权。因此,可以使用访问控制列表(ACL)来分类处理这样的的漏洞。

3.4 情景四:信息泄露或数据篡改漏洞

信息泄露和数据篡改漏洞允许非法用户或代码修改系统内的数据,以实现恶意攻击或将受保护的数据暴露给到没有权限访问该数据的用户。对于这些漏洞的FSR规则也在ACL中有描述。

4 结语

本文中所描述的方法可以大大降低误报率,并且提供了一种有效的、仅通过检测可利用的安全流量对源码进行自动化安全审计的解决方案。这种方法避免了检测攻击者无法通过输入操作、接口或者其他环境条件进入的不可利用性安全流量,从而可以减少源代码审计分析的开销,尤其是对于大型应用程序。基于参考树的FSR信息,该方法也可以对修改有漏洞的源码提供精确的指导。

参考文献

[1]徐有福,文伟平,万正苏.基于漏洞模型检测的安全漏洞挖掘方法研究[J].信息网络安全,2011(8):72-75.

[2]DHURJATI D,ADVE V S.Backwards-compatible array bounds checking for C with very low overhead[C]//Proceedings of the28th International Conference on Software Engineering.Shanghai,China:[s.n.],2006:162-171.

[3]赵国亮,蒋勇,孔令兵.用代码分析技术检测软件缺陷[J].计算机工程与设计,2011(8):2741-2744.

[4]戴凌宸,张朕荣,黎丰泽.传统的软件测试方法浅析[J].科技风,2011(16):53-55.

[5]陈晓芳.几种常见软件可靠性测试方法综述及应用对比[J].科技信息,2007(17):6-8.

[6]董洁,孙惠娟.软件测试方法及面向对象软件的测试[J].河南科技,2011(17):22-23.

[7]WAGNER D,FOSTER J,BREWER E,et al.A first step towards automated detection of buffer overrun vulnerabilities[C]//Proceedings of ISOC Network and Distributed System Security Symposium.[S.l.]:ISOC,2000:1-15.

代码优化范文

代码优化范文(精选8篇)代码优化 第1篇一、正确使用变量提高运行速度1. 强制声明变量在VBE编辑器中的菜单“工具-选项”对话框中“编辑器...
点击下载文档文档内容为doc格式

声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。如若本站内容侵犯了原著者的合法权益,可联系本站删除。

确认删除?
回到顶部