跟随计算器的脚步:SpeedCrunch

图片4

在这里,我们将继续探索计算器的代码! 今天,我们将看看名为SpeedCrunch的项目,它是第二受欢迎的免费计算器。

引言


SpeedCrunch是一款高精度科学计算器,具有快速,键盘驱动的用户界面。 它是免费的开源软件,根据GPL许可,可在Windows,Linux和macOS上运行。

源代码可在BitBucket上获得 。 生成文档令我有些失望,该文档可能会更详细。 它说您需要“ Qt 5.2或更高版本”来构建项目,但实际上它需要一些特定的程序包,这很难从CMake日志中找出。 顺便说一句,将Dockerfile包含到项目中以使用户更容易地设置开发环境已成为当今的好习惯。

这是Cloc实用程序的输出,显示SpeedCrunch与其他计算器的比较:

图片2

其他项目的错误评论:


使用PVS-Studio静态分析仪进行分析。 这是用于软件质量控制以及错误和潜在漏洞检测的解决方案包。 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,但在下一次迭代时将设置为false。 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。因此,在下一个条件语句中,其值肯定不同于零,这是分析器警告的内容。

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指针在检查之前被取消引用。 看来这里有些错误。 由于取消引用之前有许多条件,因此该bug不会经常出现,但这样做时会产生巨大的影响。

被零除


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并在您自己的“计算器”上进行尝试。 :-)

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


All Articles