遵循计算器的踪迹:SpeedCrunch

图片4

计算器代码的研究仍在继续! 在这次审查中,将审查SpeedCrunch项目-在免费计算器中第二受欢迎的项目。

引言


SpeedCrunch是一款具有快速用户界面的精密键盘驱动型科学计算器。 这是Windows,Linux和macOS上的免费开源软件。

源代码托管在BitBucket上 。 我真的不喜欢汇编文档,我认为应该更详细地编写。 要求指定“ Qt 5.2或更高版本”,尽管需要几个特定的​​软件包,但从CMake日志中不容易学习。 顺便说一句,现在,将Dockerfile应用于项目以快速配置所需的开发人员环境是一种很好的做法。

为了与其他计算器进行比较,我带来了Cloc实用程序的输出:

图片2


其他项目中的错误的评论:


PVS-Studio被用作静态分析工具。 这是一组用于代码质量控制,搜索错误和潜在漏洞的解决方案。 支持的语言包括:C,C ++,C#和Java。 该分析仪可以在Windows,Linux和macOS上启动。

循环中的奇怪逻辑


V560条件表达式的一部分始终为true :! RuleFound。 evaluator.cpp 1410

void Evaluator::compile(const Tokens& tokens) { .... while (!syntaxStack.hasError()) { bool ruleFound = false; // <= // Rule for function last argument: id (arg) -> arg. if (!ruleFound && syntaxStack.itemCount() >= 4) { // <= Token par2 = syntaxStack.top(); Token arg = syntaxStack.top(1); Token par1 = syntaxStack.top(2); Token id = syntaxStack.top(3); if (par2.asOperator() == Token::AssociationEnd && arg.isOperand() && par1.asOperator() == Token::AssociationStart && id.isIdentifier()) { ruleFound = true; // <= syntaxStack.reduce(4, MAX_PRECEDENCE); m_codes.append(Opcode(Opcode::Function, argCount)); #ifdef EVALUATOR_DEBUG dbg << "\tRule for function last argument " << argCount << " \n"; #endif argCount = argStack.empty() ? 0 : argStack.pop(); } } .... } .... } 

注意ruleFound变量。 在每次迭代时,将其设置为false。 但是,如果您查看整个循环的主体,则在某些条件下,此变量将设置为true,但在循环的新迭代中将不考虑该变量。 最有可能的是, 必须在循环之前声明ruleFound变量。

可疑比较


V560条件表达式的一部分始终为true:m_scrollDirection!=0。resultdisplay.cpp 242

 void ResultDisplay::fullContentScrollEvent() { QScrollBar* bar = verticalScrollBar(); int value = bar->value(); bool shouldStop = (m_scrollDirection == -1 && value <= 0) || (m_scrollDirection == 1 && value >= bar->maximum()); if (shouldStop && m_scrollDirection != 0) { // <= stopActiveScrollingAnimation(); return; } scrollLines(m_scrollDirection * 10); } 

如果shouldStop变量为true ,则m_scrollDirection变量将具有以下两个值之一:-1或1。因此,在以下条件语句中, m_scrollDirection变量的值肯定不会为零,分析器对此进行警告。

V668测试“ item”指针是否为null没有意义,因为使用“ new”运算符分配了内存。 如果内存分配错误,将生成异常。 editor.cpp 998

 void EditorCompletion::showCompletion(const QStringList& choices) { .... for (int i = 0; i < choices.count(); ++i) { QStringList pair = choices.at(i).split(':'); QTreeWidgetItem* item = new QTreeWidgetItem(m_popup, pair); if (item && m_editor->layoutDirection() == Qt::RightToLeft) item->setTextAlignment(0, Qt::AlignRight); .... } .... } 

使用new运算符分配QTreeWidgetItem类型的对象的内存。 这意味着如果无法进行动态内存分配,则将引发异常std :: bad_alloc() 。 因此,检查项目指针是多余的,可以删除。

潜在的NULL取消引用


V595在针对nullptr进行验证之前,已使用了“ ioparams”指针。 检查线:969,983。floatio.c 969

 int cattokens(....) { .... if (printexp) { if (expbase < 2) expbase = ioparams->expbase; // <= .... } dot = '.'; expbegin = "("; expend = ")"; if (ioparams != NULL) // <= { dot = ioparams->dot; expbegin = ioparams->expbegin; expend = ioparams->expend; } .... } 

在检查有效性之前,先取消引用ioparams指针。 很有可能,潜在的错误潜入了代码中。 由于取消引用是在几种情况下进行的,因此该问题很少会出现,但会准确地显示出来。

被零除


V609除以零。 分母范围[0..4]。 第266章

 static int lgbase( signed char base) { switch(base) { case 2: return 1; case 8: return 3; case 16: return 4; } return 0; // <= } static void _setlongintdesc( p_ext_seq_desc n, t_longint* l, signed char base) { int lg; n->seq.base = base; lg = lgbase(base); // <= n->seq.digits = (_bitlength(l) + lg - 1) / lg; // <= n->seq.leadingSignDigits = 0; n->seq.trailing0 = _lastnonzerobit(l) / lg; // <= n->seq.param = l; n->getdigit = _getlongintdigit; } 

lgbase函数允许返回空值,然后通过该值执行除法。 可以将值2、8和16以外的任何内容传递给该函数。

未定义的行为


V610未定义的行为。 检查移位运算符“ <<”。 左操作数'(〜0)'为负。 floatlogic.c 64

 static char _signextend( t_longint* longint) { unsigned mask; signed char sign; sign = _signof(longint); mask = (~0) << SIGNBIT; // <= if (sign < 0) longint->value[MAXIDX] |= mask; else longint->value[MAXIDX] &= ~mask; return sign; } 

零反转的结果放置在符号类型int中 ,因此结果将为负数,然后对其执行移位。 向左移动负数是未定义的行为。

危险场所的完整列表:

  • V610未定义的行为。 检查移位运算符“ <<”。 左操作数'(-1)'为负。 floatnum.c 289
  • V610未定义的行为。 检查移位运算符“ <<”。 左操作数'(-1)'为负。 floatnum.c 325
  • V610未定义的行为。 检查移位运算符“ <<”。 左操作数'(-1)'为负。 floatnum.c 344
  • V610未定义的行为。 检查移位运算符“ <<”。 左操作数'(-1)'为负。 floatnum.c 351

未封闭的HTML标签


V735可能是不正确的HTML。 遇到“ </ body>”结束标记,而应使用“ </ div>”标记。 book.cpp 127

 static QString makeAlgebraLogBaseConversionPage() { return BEGIN INDEX_LINK TITLE(Book::tr("Logarithmic Base Conversion")) FORMULA(y = log(x) / log(a), log<sub>a</sub>x = log(x) / log(a)) END; } 

正如C / C ++代码经常发生的那样,从源头上还不清楚什么,所以让我们来看一下此片段的预处理代码:

图片3



分析仪检测到未关闭的div标签。 该文件中有许多html代码片段,现在开发人员应该对其进行额外检查。

以下是使用PVS-Studio发现的一些可疑的地方:

  • V735可能是不正确的HTML。 遇到“ </ td>”结束标记,而“ </ sub>”标记是预期的。 book.cpp 344
  • V735可能是不正确的HTML。 遇到“ </ td>”结束标记,而“ </ sub>”标记是预期的。 book.cpp 347

赋值运算符


V794应该保护赋值运算符免受'this ==&other'的影响。 数量cpp 373

 Quantity& Quantity::operator=(const Quantity& other) { m_numericValue = other.m_numericValue; m_dimension = other.m_dimension; m_format = other.m_format; stripUnits(); if(other.hasUnit()) { m_unit = new CNumber(*other.m_unit); m_unitName = other.m_unitName; } cleanDimension(); return *this; } 

建议通过比较指针来考虑将对象分配给自身的情况。

换句话说,应将以下两行代码添加到函数主体的开头:

 if (this == &other) return *this; 

温馨提示


V601'false '值隐式转换为整数类型。 cmath.cpp 318

 /** * Returns -1, 0, 1 if n1 is less than, equal to, or more than n2. * Only valid for real numbers, since complex ones are not an ordered field. */ int CNumber::compare(const CNumber& other) const { if (isReal() && other.isReal()) return real.compare(other.real); else return false; // FIXME: Return something better. } 

有时,在对我们文章的评论中,他们建议某些警告是针对不完整的代码发出的。 是的,它确实发生了,但是实际上它是直接写在它上面的。

结论


已有三个计算器的评论:Windows计算器,Qalculate! 和SpeedCrunch。 我们准备继续研究流行的计算器的代码。 您可以提供项目进行验证,因为软件的等级并不总是反映真实情况。

通过下载PVS-Studio并尝试您的项目来检查“计算器” :-)



如果您想与说英语的读者分享这篇文章,请使用以下链接:Svyatoslav Razmyslov。 跟随计算器的脚步:SpeedCrunch

Source: https://habr.com/ru/post/zh-CN444322/


All Articles