التالية على خطى الآلات الحاسبة: 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 جزء من التعبير الشرطي صحيح دائمًا:! 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 جزء من التعبير الشرطي صحيح دائمًا: 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 صحيحة ، فسيأخذ المتغير m_scrollDirection إحدى القيمتين: -1 أو 1. لذلك ، ستكون قيمته بالتأكيد مختلفة عن الصفر في البيان الشرطي التالي ، وهو ما يحذر المحلل منه.

V668 لا يوجد أي معنى في اختبار مؤشر "العنصر" مقابل قيمة خالية ، حيث تم تخصيص الذاكرة باستخدام عامل التشغيل "الجديد". سيتم إنشاء الاستثناء في حالة خطأ تخصيص الذاكرة. 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); .... } .... } 

يتم تخصيص الذاكرة لكائن من النوع QTreeWidgetItem باستخدام عامل التشغيل الجديد . فهذا يعني أن فشل تخصيص ذاكرة سيؤدي إلى رمي استثناء std :: bad_alloc () . التحقق من مؤشر العنصر هو ، بالتالي ، زائدة ويمكن إزالته.

محتمل NULL Dereference


V595 تم استخدام مؤشر "ioparams" قبل أن يتم التحقق منه ضد nullptr. خطوط التحقق: 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 قبل الفحص. يبدو أن هناك بعض الأخطاء هنا. بما أن dereference يسبقه عدد من الشروط ، فلن يظهر الخطأ كثيرًا ، لكن سيكون له تأثير كبير عندما يحدث.

القسمة على صفر


V609 قسّم على صفر. نطاق المقام [0..4]. floatconvert.c 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 يجب حماية مشغل المهمة من حالة "هذا == & غيرها". quantity.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 القيمة "الخاطئة" يتم ضمنيًا ضمنا لنوع الأعداد الصحيحة. 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 Calculator و Qalculate! و SpeedCrunch - ولن نتوقف. لا تتردد في اقتراح مشاريع تريد أن نتحقق منها لأن تصنيفات البرامج لا تعكس دائمًا الحالة الحقيقية للأشياء.

مرحبًا بك في تنزيل PVS-Studio وجربه على "الحاسبة" الخاصة بك. :-)

Source: https://habr.com/ru/post/ar444324/


All Articles