来源:https://www.viva64.com/en/general-analysis/

通用分析

PVS - Studio产品包含了一套通用静态分析规则,用于检测在C / C ++/ C+11应用程序中大范围内的各种缺陷。

通用的规则集帮助您发现逻辑错误,拼写错误,导致访问冲突的代码片段,STL库算法的不正确使用等。

当选择PVS – Studio的执行规则时,我们首先要从他们的相关性开始。我们自觉拒绝执行像MISRA这样的标准,因为那里的大多数建议描述没有什么帮助,开发人员使用现代工具,如Visual StudioSTLMFCQtBoost等的库。在PVS-Studio中,我们尝试实施更好的诊断,不仅考虑C / C ++语言的细节,还考虑更高层次的模式以及代码和库的相互作用。许多检查是独一无二的,目前还没有被我们所熟悉的分析仪实现过。

PVS - Studio中检测到的错误模式是千差万别的。因此,我们接下来只说明工具有可以检测的某些类型错误。如果您想了解分析仪检测到的其他类型的错误,请参考相关文档。下面给出的错误样本均取自真实的项目。

当“+1”写错地方时,发生的错误

if ((t=(char *)realloc(next->name, strlen(name+1))))

{

  next->name=t;

  strcpy(next->name, name);

}

分析仪在这里发现了程序员不小心犯下的错误。程序分配比实际需要少2个字节的内存。为了纠正错误,你应该把“+1”从括号中拿出来。这是正确的代码:"realloc(next->name, strlen(name) + 1)"

矩阵清理不彻底

#define CONT_MAP_MAX 50

int _iContMap[CONT_MAP_MAX];

memset(_iContMap, -1, CONT_MAP_MAX);

这个错误是指缓冲区欠载/超载类的。当只是处理数组的部分,或者反过来,修改数组外的内存时,这样的错误就会发生。在此示例中,memset()函数只清除小部分的_iContMap阵列。这里的错误是缺少sizeof():

memset(_iContMap, -1, CONT_MAP_MAX * sizeof(_iContMap[0]));

缺少非关联指针

if (pSlash != NULL) {

  pSlash++;

  if (pSlash != '\0') {

    ...

分析仪通常可以帮助检测可疑的指针处理,例如,下面的示例。这里指针跟0比较。如果该指针不等于0,它就+1,再次和0比较。这是很奇怪的操作。他们当然包含一个错误,因为非关联指针操作丢失:“if(!* pSlash='\ 0'{”

不正确的循环

int i, destlen = 0, l, k;

for (i=0; i<srclen; i++)

{

  ...

  for (k=i; i<srclen; k++)

  {

    if (src[k]=='>')

      break;

  }

  i = k;

}

分析仪在这段代码中发现了一个错字: “it”变量代替“K”,用在嵌套循环比较中。此代码是很容易造成内存访问到正在处理的数组以外。

写错变量名

class CSize : public SIZE

{

  ...

  CSize(POINT pt) { cx = pt.x; cx = pt.y; }

这个错误在项目中被发现,程序员使用自己的类CSIZE。由于书写错误,结果一个相同的变量被分配不同的值。这引起分析仪的注意。第二次分配操作的正确代码是:“cy = pt.y;”。

Windows API使用不当

OPENFILENAME lofn;

...

lofn.lpstrFilter = L"Equalizer Preset (*.feq)\0*.feq";

Windows API的结构中,字符串指针必须以两个零结尾。在此示例中,在OPENFILENAME结构的lpstrFilter成员是一个指针。此代码将在过滤文件中的对话领域中产生的垃圾。这是正确的代码:

lofn.lpstrFilter = L"Equalizer Preset (*.feq)\0*.feq\0";

我们手工在行末写“0”,因为编译器将会添加多一个0

标准算法处理不当

我们上面所讨论的所有示例是低级错误。现在让我们看看在PVS-Studio实施的检查,处理更高层次的概念,如代码块或算法。

void unregisterThread() {

  Guard<TaskQueue> g(_taskQueue);

  std::remove(_threads.begin(), _threads.end(), ThreadImpl::current());

}

std::remove函数不从存储器中删除的项目。它只是转移项目和返回到垃圾的迭代器的起点。假设我们有矢量<int>存储器,它包含项目1,2,3,1,2,3,1,2,3。假如我们执行代码"remove( v.begin(), v.end(), 2 )",容器将包含项目1,3,1,3,?,?,?,这里“?”是一些垃圾。此外,该函数将返回第一个垃圾项目的迭代器,所以如果我们要去除这些垃圾项目,我们必须编写以下代码:"v.erase(remove(v.begin(), v.end(), 2), v.end())".这是正确的代码:

auto trash = std::remove(_threads.begin(), _threads.end(),

                         ThreadImpl::current());

_threads.erase(trash, _threads.end());

使用复制-粘贴方法编写代码的错误

void KeyWordsStyleDialog::updateDlg()

{

  ...

  Style & w1Style =

    _pUserLang->_styleArray.getStyler(STYLE_WORD1_INDEX);

  styleUpdate(w1Style, _pFgColour[0], _pBgColour[0],

    IDC_KEYWORD1_FONT_COMBO, IDC_KEYWORD1_FONTSIZE_COMBO,

    IDC_KEYWORD1_BOLD_CHECK, IDC_KEYWORD1_ITALIC_CHECK,

    IDC_KEYWORD1_UNDERLINE_CHECK);

 

  Style & w2Style =

    _pUserLang->_styleArray.getStyler(STYLE_WORD2_INDEX);

  styleUpdate(w2Style, _pFgColour[1], _pBgColour[1],

    IDC_KEYWORD2_FONT_COMBO, IDC_KEYWORD2_FONTSIZE_COMBO,

    IDC_KEYWORD2_BOLD_CHECK, IDC_KEYWORD2_ITALIC_CHECK,

    IDC_KEYWORD2_UNDERLINE_CHECK);

 

  Style & w3Style =

    _pUserLang->_styleArray.getStyler(STYLE_WORD3_INDEX);

  styleUpdate(w3Style, _pFgColour[2], _pBgColour[2],

    IDC_KEYWORD3_FONT_COMBO, IDC_KEYWORD3_FONTSIZE_COMBO,

    IDC_KEYWORD3_BOLD_CHECK, IDC_KEYWORD3_BOLD_CHECK,

    IDC_KEYWORD3_UNDERLINE_CHECK);

 

  Style & w4Style =

    _pUserLang->_styleArray.getStyler(STYLE_WORD4_INDEX);

  styleUpdate(w4Style, _pFgColour[3], _pBgColour[3],

    IDC_KEYWORD4_FONT_COMBO, IDC_KEYWORD4_FONTSIZE_COMBO,

    IDC_KEYWORD4_BOLD_CHECK, IDC_KEYWORD4_ITALIC_CHECK,

    IDC_KEYWORD4_UNDERLINE_CHECK);

  ...

}

要发现这段代码中的错误是非常困难的。但PVS - Studio的耐心和学识:“V525:代码包含相似的块的集合。在行576580584588”中检查项目'7''7''6''7'。让我们缩短的代码,使错误更明显:

styleUpdate(...

  IDC_KEYWORD1_BOLD_CHECK, IDC_KEYWORD1_ITALIC_CHECK,

  ...);

styleUpdate(...

  IDC_KEYWORD2_BOLD_CHECK, IDC_KEYWORD2_ITALIC_CHECK,

  ...);

styleUpdate(...

  IDC_KEYWORD3_BOLD_CHECK, IDC_KEYWORD3_BOLD_CHECK,

  ...);

styleUpdate(...

  IDC_KEYWORD4_BOLD_CHECK, IDC_KEYWORD4_ITALIC_CHECK,

  ...);

此代码最有可能是使用了复制粘贴的方法,导致使用IDC_KEYWORD3_BOLD_CHECK而不是IDC_KEYWORD3_ITALIC_CHECK。您在诊断消息中看到的数字出现这样的宏:

#define IDC_KEYWORD1_ITALIC_CHECK (IDC_KEYWORD1 + 7)

#define IDC_KEYWORD3_BOLD_CHECK (IDC_KEYWORD3 + 6)

最后一个样本具有特别重要的意义,因为它表明,PVS - Studio 分析器同时处理整个大的代码片段,检测重复的结构,并根据启发式方法,认为这代码具有潜在危险。

使用通用分析的好处

静态分析最宝贵的是,你可以在开发初期检测一些错误。一个错误越早检测到,消除它就越容易、快速、便宜。这减少了测试和维护阶段的费用。

静态分析仪处理源代码,检查程序执行中所有可能的方式。因此,您可能会发现在很少使用的代码中或在你打算使用该项目的下一阶段的代码中的错误。

PVS - Studio是一个现代分析仪,没有沉重的过往负担。它积极地发展和学习,检测现在程序员使用Microsoft Visual C + +遇到的错误的新模式。您可能在这一领域帮助我们,并为我们提供你自己发明的诊断规则。在反馈页面给我们发送您的建议。