for (CORBA::ULong i = 0; i < mnsInfos.length(); ++i)


  这里提出第二条检视规则:输入参数参与循环判定或条件判定时,需对该参数取值进行分析,检查程序是否存在条件遗漏。

  这里的取值情况比较明显,只需检查当mnsInfos.length()取值为0时程序是否有正常处理即可。从代码看出,取值为0时,跳过整个循环,输出参数(序列)长度都置为0,如果这样的处理符合设计模型,没有问题。

  请注意斜体字部分,对代码实现是否有问题不应简单、孤立地判定,而应结合设计与需求进行检查,切记。

  接下来是一个赋值语句,将mnsInfos[i].monsterInfos赋值给局部变量mnsStatusSeq。

  这里提出第三条检视规则:变量以某种方式与输入参数建立关联的,等同于输入参数对待。

  这条规则的意思是,变量与输入参数关联后,同样适用于第一、第二两条规则。这里有点递归的意思。

  因此,对mnsStatusSeq的后续使用,同样要与输入参数mnsInfos一样进行分析,由于分析原则一样,不作重复说明。

  另外需要指出的是,在分析hrdId时,实际上有一个变量mnsTypes与hrdId建立了关联——尽管不是赋值,但是通过函数调用产生了关联(mnsTypes是函数getMonsterType的出参),因此对mnsTypes变量同样要进行相应的分析。

  在此之后直接出现mnsInfos的地方全都是赋值,同样遵循规则三处理。

  对全部的(2个)直接输入参数分析完成后,可以递归地对所有的“间接输入参数”(即前面提及的,通过某种方式与输入参数建立关联的局部变量,如mnsStatusSeq、mnsTypes等)进行分析。

  所有输入参数分析完毕后,第二步算是完成了。

  第三步:

  接下来需要覆盖的,是真正的“祸患之源”,即“非常规输入”,或者叫“隐性输入”。

  我们来看第二段代码:

  示例代码段二


virtual ::std::auto_ptr<World::Quest::TaskResult>
run(World::QuestMgr::RoleProxy& roleProxy) {
QuestPos notExistQuests;
try
{
World::StateMgr::StateManager& stateMgr =
Frame::kernelModule<World::StateMgr::StateManager>(owner_.provider().kernel());
//获取Quest
CORBA::ULong processPos = 0;
for(; (!cancel_) && processPos < questIDs_.length(); processPos++)
{
if(notExistQuests.find(processPos) != notExistQuests.end()) {
continue;
}
try
{
World::Model::Quest questObj =
Frame::kernelModule<World::Model::QuestCvt::QuestConvertMgr>(
owner_.provider().kernel()).cvtQuestById(
World::Model::Basic::Quest(questIDs_[processPos].in()));
owner_.appendQuestState(
stateMgr.getQuest(
stateName_,
questObj));
}
catch(...) {
STATEMGR_ERROR("acquire quest state catch exception");
}
}
}
STATEMGR_REPORT_EXCEPTION_PERISH("StateQueryOp::run catch exception");
owner_.setIsDone(true);
return World::Quest::TaskResult::_success();
}
  在以上的代码段里,你能找到哪些“隐性输入”?

  我个人将“隐性输入”划分为以下四个层次:

  层次一,我们可以很容易找到:questIDs_.length()、quest的取值、notExistQuests.find()的返回值和stateMgr. getQuest ()的返回值。从宏观上说,凡是来自于本程序段之外的数据都应视为本程序段的输入,在进行程序走向和处理分析时,都需要予以关注。

  如上所述的四个数值都属于该代码段的“非常规输入”,因此对它们可能的取值,都应该分别分析是否影响程序走向,是否有正确的处理。

  questIDs_.length()的处理可以参考常规输入的处理。

  questObj的取值则需要参考cvtQuestById函数的定义(或向该函数的编写者询问相应信息),要考虑有效Id和无效Id的情况(即对象找不到)。

  同样notExistQuests.find()也会返回两种结果。

  而getQuest ()的结果要更复杂一些,包括正常的对象列表,空对象列表(没找到)和异常三种(即输入参数无效,questObj的取值是无效对象)。当然,在处理输入的questObj无效时,getQuest ()的返回可能是空对象列表,也可能是抛出异常,具体情况还是要再对getQuest ()进行分析才能确定。

  特别地,对于调用外部函数获取的输入值,如果代码编写者自己并不清楚其可能的取值范围,需要从设计文档或该外部函数设计者处获取信息

  层次二,还有其他的“非常规输入”吗?结合对“输入”的定义,我们能够发现cancel_也是个输入值。尽管它是当前类定义的成员变量,对“类”这个单元来说是属于内部数据,但是对当前被检视的run函数而言还是个“外来户”。当然,根据这个标准,那些“下划线一族”也都是“外来户”,包括questIDs_、owner_。