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

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 قبل التحقق من صحته. على الأرجح ، تسلل خطأ محتمل إلى الكود. نظرًا لأن إلغاء التسجيل يخضع لشروط عديدة ، يمكن أن تظهر المشكلة بشكل نادر ، ولكن بدقة.

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


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. } 

في بعض الأحيان في التعليقات على مقالاتنا تشير إلى أن بعض التحذيرات تصدر على رمز غير مكتمل. نعم ، يحدث ذلك ، لكن عندما يكون الأمر حقًا ، يتم كتابته مباشرةً حول هذا الموضوع.

استنتاج


الاستعراضات المتاحة بالفعل من ثلاث آلات حاسبة: حاسبة ويندوز ، Qalculate! و SpeedCrunch. نحن على استعداد لمواصلة البحث في مدونة الآلات الحاسبة الشعبية. يمكنك تقديم مشاريع للتحقق منها ، لأن تصنيفات البرنامج لا تعكس دائمًا الصورة الحقيقية.

تحقق من "الحاسبة" عن طريق تنزيل PVS-Studio ومحاولة مشروعك :-)



إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فالرجاء استخدام الرابط الخاص بالترجمة: Svyatoslav Razmyslov. التالية على خطى الآلات الحاسبة: SpeedCrunch

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


All Articles