
تقنيات الكمبيوتر الحديثة ، والحلول التقنية والبرمجيات - كل هذا يسهل ويسرع إلى حد كبير تنفيذ العديد من البحوث العلمية. غالبًا ما تكون المحاكاة الحاسوبية هي الطريقة الوحيدة لاختبار العديد من النظريات. البرامج العلمية لها خصائصها الخاصة. على سبيل المثال ، غالبًا ما تخضع مثل هذه البرامج لاختبار شامل للغاية ، لكنها غير موثقة بشكل جيد. ومع ذلك ، يتم كتابة البرامج من قبل الناس ، والناس يرتكبون الأخطاء. قد تشكك أخطاء البرامج العلمية في البحث بأكمله. تسرد هذه المقالة العشرات من المشكلات الموجودة في رمز حزمة برامج NCBI Genome Workbench.
مقدمة
يوفر
منضدة الجينوم NCBI للباحثين مجموعة واسعة من الأدوات لدراسة البيانات الوراثية وتحليلها. يمكن للمستخدمين البحث ومقارنة البيانات من عدة مصادر ، بما في ذلك قواعد بيانات NCBI (المركز الوطني لمعلومات التكنولوجيا الحيوية) أو بياناتهم الشخصية.
كما ذكرنا سابقًا ، عادةً ما يتم تغطية البرامج العلمية بشكل جيد من خلال اختبارات الوحدة. عند التحقق من هذا المشروع ، تم استبعاد 85 دليلاً بملفات اختبار من التحليل. هذا حوالي ألف ملف. ربما يرجع ذلك إلى متطلبات اختبار الخوارزميات المعقدة المختلفة التي تم اختراعها لدراسات مختلفة. لكن جودة باقي الشفرة (وليس الاختبار) ليست بمستوى عالٍ كما نود. ومع ذلك ، كما هو الحال في أي مشروع لم يهتموا فيه بعد بإدخال أدوات تحليل التعليمات البرمجية الثابتة :).
تم توفير البيانات لمراجعة (أو حتى البحث) من التعليمات البرمجية بواسطة محلل الكود الثابت لـ C / C ++ / C # / Java -
PVS-Studio .
رقمين فقط يدمران مشروعك
استنادًا إلى قاعدة بياناتنا للأخطاء ، والتي تصل حاليًا إلى أكثر من 12 ألف مثال محدد ، نلاحظ ونصف أنماطًا محددة لكتابة التعليمات البرمجية التي تؤدي إلى العديد من الأخطاء. على سبيل المثال ، أجرينا الدراسات التالية:
- تأثير السطر الأخير ؛
- أخطر وظيفة في عالم C / C ++ ؛
- التعبيرات المنطقية في C / C ++. ما مدى خطأ المهنيين ؛
- يعيش الشر في وظائف المقارنة .
يمثل هذا المشروع بداية وصف النمط الجديد. نحن نتحدث عن الرقمين
1 و
2 في أسماء المتغيرات ، على سبيل المثال ،
file1 و
file2 ، إلخ. من السهل جدًا الخلط بين متغيرين من هذا القبيل. هذه حالة خاصة من الأخطاء الإملائية في التعليمات البرمجية ، ولكن أحد هذه الأخطاء يؤدي إلى الرغبة في العمل مع متغيرات تحمل نفس الاسم ، والتي تختلف فقط بالأرقام 1 و 2 في نهاية الاسم.
بالنظر إلى المستقبل قليلاً ، سأقول أنه تم تأكيد جميع الدراسات المذكورة أعلاه في رمز هذا المشروع: D.
خذ بعين الاعتبار المثال الأول من مشروع Genome Workbench:
V501 توجد تعبيرات فرعية متطابقة "(! Loc1.IsInt () &&! Loc1.IsWhole ())" إلى اليسار وإلى يمين "||" عامل. nw_aligner.cpp 480
CRef<CSeq_align> CNWAligner::Run(CScope &scope, const CSeq_loc &loc1, const CSeq_loc &loc2, bool trim_end_gaps) { if ((!loc1.IsInt() && !loc1.IsWhole()) || (!loc1.IsInt() && !loc1.IsWhole())) { NCBI_THROW(CException, eUnknown, "Only whole and interval locations supported"); } .... }
نرى متغيرين باسم
loc1 و
loc2 . وكذلك خطأ في الكود: لا
يتم استخدام متغير
loc2 ، لأنه بدلاً من ذلك يتم استخدام
loc1 مرة أخرى.
مثال آخر:
V560 دائمًا ما يكون جزء من التعبير الشرطي خطأ: s1.IsSet (). valid_biosource.cpp 3073
static bool s_PCRPrimerSetLess(const CPCRPrimerSet& s1, const CPCRPrimerSet& s2) { if (!s1.IsSet() && s1.IsSet()) { return true; } else if (s1.IsSet() && !s2.IsSet()) { return false; } else if (!s1.IsSet() && !s2.IsSet()) { return false; } else if (s1.Get().size() < s2.Get().size()) { return true; } else if (s1.Get().size() > s2.Get().size()) { return false; } else { ..... }
خلط السطر الأول من الكود بين المتغيرين
s1 و
s2 . استنادًا إلى الاسم ، هذه وظيفة مقارنة. لكن مثل هذا الخطأ يمكن أن يكون في أي مكان ، لأنه من خلال تسمية المتغيرين
رقم 1 ورقم 2 ، سيبرمج المبرمج بالتأكيد خطأ في المستقبل. وكلما زاد استخدام هذه الأسماء في دالة ، زاد احتمال حدوث خطأ.
أخطاء إملائية أخرى ولصق ولصق
V501 توجد تعبيرات فرعية متطابقة إلى يسار ويمين عامل التشغيل '! =': bd.bit_.bits [i]! = Bd.bit_.bits [i] bm.h 296
bool compare_state(const iterator_base& ib) const { .... if (this->block_type_ == 0 { if (bd.bit_.ptr != ib_db.bit_.ptr) return false; if (bd.bit_.idx != ib_db.bit_.idx) return false; if (bd.bit_.cnt != ib_db.bit_.cnt) return false; if (bd.bit_.pos != ib_db.bit_.pos) return false; for (unsigned i = 0; i < bd.bit_.cnt; ++i) { if (bd.bit_.bits[i] != bd.bit_.bits[i]) return false; } } .... }
أعتقد أنه بعد كل عمليات التحقق ، تكون أحجام صفائف
البت للكائنات bd.bit_ و
ib_db.bit_ متساوية. لذلك ، كتب مؤلف الشفرة دورة واحدة للمقارنة بين عناصر صفائف
البتات ، ولكنه قام بخطأ مطبعي باسم أحد الكائنات المقارنة. ونتيجة لذلك ، يمكن اعتبار الأشياء المقارنة متساوية عن طريق الخطأ في بعض المواقف.
هذا المثال يستحق مقال "
الشر يعيش في وظائف المقارنة ".
V501 هناك تعبيرات فرعية متطابقة "CFieldHandler :: QualifierNamesAreEquivalent (field، kFieldTypeSeqId)" إلى اليسار وإلى يمين "||" عامل. حقل_القلب. cpp 152
bool CFieldHandlerFactory::s_IsSequenceIDField(const string& field) { if ( CFieldHandler::QualifierNamesAreEquivalent(field, kFieldTypeSeqId) || CFieldHandler::QualifierNamesAreEquivalent(field, kFieldTypeSeqId)) { return true; } else { return false; } }
على الأرجح ، أحد الشيكات غير ضروري. لم أجد في متغيرات التعليمات البرمجية المشابهة لـ
kFieldTypeSeqId . ومع ذلك ، يمكن استدعاء دالة إضافية بسبب عامل "||" ، مما يؤدي إلى انخفاض الأداء.
هناك نوعان آخران من نفس النوع من الأماكن مع تحذير محلل ، يتطلب التحقق:
- V501 توجد تعبيرات فرعية متطابقة 'uf-> GetData (). IsBool ()' إلى اليسار وإلى يمين عامل التشغيل "&&". 1711
- V501 توجد تعبيرات فرعية متطابقة 'uf-> GetData (). IsBool ()' إلى اليسار وإلى يمين عامل التشغيل "&&". 1735
V766 تمت إضافة عنصر بنفس المفتاح "kArgRemote". 3262
void CBlastAppArgs::x_IssueWarningsForIgnoredOptions(const CArgs& args) { set<string> can_override; .... can_override.insert(kArgOutputFormat); can_override.insert(kArgNumDescriptions); can_override.insert(kArgNumAlignments); can_override.insert(kArgMaxTargetSequences); can_override.insert(kArgRemote);
اكتشف المحلل إضافة قيمتين متطابقتين إلى الحاوية
المحددة . تذكر أن هذه الحاوية تقوم بتخزين القيم الفريدة فقط ، لذلك لا تتم إضافة التكرارات إليها.
غالبًا ما تتم كتابة الرمز مثل الرمز أعلاه باستخدام طريقة النسخ واللصق. قد تكون هناك ببساطة قيمة إضافية ، أو ربما نسي المؤلف إعادة تسمية أحد المتغيرات عند نسخه. عندما تقوم بإزالة مكالمة إضافية
لإدراجها ، يتم تحسين الرمز قليلاً ، ومع ذلك ، ليس مهمًا. الأهم من ذلك ، قد يتم إخفاء خطأ فادح هنا بسبب عنصر مفقود في المجموعة.
V523 العبارة "then" تعادل جزء الكود التالي. 1105
bool CVcfReader::xAssignFeatureLocationSet(....) { .... if (data.m_SetType == CVcfData::ST_ALL_DEL) { if (data.m_strRef.size() == 1) {
تحتوي الوظيفة على أجزاء رمز كبيرة ومتطابقة تمامًا. ومع ذلك ، فإنها تحتوي على العديد من التعليقات المصاحبة. لم تتم كتابة الرمز على النحو الأمثل والمربك ، وربما يحتوي على خطأ.
تبدو قائمة الأماكن المشبوهة بالكامل مع عبارة if-else كما يلي:
- V523 تعادل العبارة "then" العبارة "else". blk.c 2142
- V523 العبارة "then" تعادل جزء الكود التالي. 379
- V523 العبارة "then" تعادل جزء الكود التالي. 1414
- V523 تعادل العبارة "then" العبارة "else". seqdbvol.cpp 1922
- V523 تعادل العبارة "then" العبارة "else". 466
- V523 العبارة "then" تعادل جزء الكود التالي. انفجار_جهاز.ج 1917
- V523 تعادل العبارة "then" العبارة "else". 420 ج
- V523 تعادل العبارة "then" العبارة "else". 636
- V523 تعادل العبارة "then" العبارة "else". 684
- V523 تعادل العبارة "then" العبارة "else". بي ام سي 333
- V523 تعادل العبارة "then" العبارة "else". 484
/ * مع الأمان من الأفضل أن يكون متحيزًا * /
V597 يمكن للمترجم حذف استدعاء دالة "memset" ، والذي يستخدم لتنظيف المخزن المؤقت "passwd_buf". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. محمد علي جابر 366
void tds_answer_challenge(....) { #define MAX_PW_SZ 14 .... if (ntlm_v == 1) { .... memset(hash, 0, sizeof(hash)); memset(passwd_buf, 0, sizeof(passwd_buf)); memset(ntlm2_challenge, 0, sizeof(ntlm2_challenge)); } else { .... } }
كما كنت قد خمنت بالفعل ، تم استخدام تعليق مضحك حول الأمان من التعليمات البرمجية في عنوان القسم.
باختصار ، سيتم إزالة وظيفة
memset بواسطة المحول البرمجي ، لأنه لم يعد يتم استخدام المخازن المؤقتة
المتدفقة . ولن تكون البيانات مثل
التجزئة أو
passwd_buf أصفارًا في الواقع. لمزيد من المعلومات حول آلية المترجم غير الواضحة ، انظر المقالة "
مسح البيانات الخاصة بأمان ".
V597 المترجم يمكن أن يحذف استدعاء دالة "memset" ، الذي يستخدم لمسح كائن "الإجابة". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. محمد علي جابر 561
static TDSRET tds7_send_auth(....) { .... memset(&answer, 0, sizeof(TDSANSWER)); return tds_flush_packet(tds); }
لم يكن هذا المثال الوحيد مع التعليقات حول "الأمن". بناءً على التعليقات ، يمكن افتراض أن الأمن مهم حقًا للمشروع. لذلك ، أرفق قائمة كاملة غير صغيرة بالمشكلات المحددة:
- V597 المترجم يمكن أن يحذف استدعاء دالة "memset" ، الذي يستخدم لمسح كائن "كومة الذاكرة المؤقتة". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. ncbi_heapmgr.c 1300
- V597 المترجم يمكن أن يحذف استدعاء دالة "memset" ، الذي يستخدم لمسح كائن "السياق". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. التحدي ج ج 167
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، الذي يستخدم لتنظيف كائن "ks". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. تحدي ج 339
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، الذي يستخدم لتنظيف كائن "md5_ctx". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. 353
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، والذي يتم استخدامه لمسح المخزن المؤقت "التجزئة". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. تحدي ج 365
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، الذي يستخدم لتنظيف كائن "ks". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. 406 ج
- V597 يمكن للمترجم حذف استدعاء الوظيفة "memset" ، والذي يستخدم لتنظيف كائن "ntlm_v2_response". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. 795 ج
- V597 المترجم يمكن أن يحذف استدعاء دالة "memset" ، الذي يستخدم لمسح كائن "الإجابة". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. ج 801
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، والذي يستخدم لتنظيف المخزن المؤقت "packet". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. رقمي 256 ج
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، والذي يستخدم لتنظيف المخزن المؤقت "packet". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. رقمي 110 ج
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، والذي يستخدم لتنظيف المخزن المؤقت "pwd". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. جيتباسارج سي 50
- V597 المترجم يمكن أن يحذف استدعاء دالة "memset" ، الذي يستخدم لمسح كائن "السياق". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. التحدي ج ج 188
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، والذي يستخدم لتنظيف المخزن المؤقت "buf". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. التحدي ج ج .243
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، والذي يستخدم لتنظيف المخزن المؤقت "ntlm_v2_hash". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. 30 ج
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، الذي يستخدم لتنظيف كائن "md5_ctx". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. تحدي ج 354
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، والذي يستخدم لتنظيف المخزن المؤقت "passwd_buf". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. تحدي ج 380
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، الذي يستخدم لتنظيف كائن "ks". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. تحدي ج ج 393
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، والذي يتم استخدامه لمسح المخزن المؤقت "التجزئة". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. تحدي ج ج 394
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، والذي يستخدم لتنظيف المخزن المؤقت "ntlm2_challenge". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. تحدي ج ج 395
- V597 يمكن للمترجم حذف استدعاء دالة "memset" ، الذي يستخدم لتنظيف كائن "ks". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. تحدي ج 419
- V597 يمكن للمترجم حذف استدعاء الوظيفة "memset" ، والذي يستخدم لتنظيف كائن "ntlm_v2_response". يجب استخدام الدالة memset_s () لمسح البيانات الخاصة. تحدي ج 556
دورات مشبوهة
V534 من المحتمل أن تتم مقارنة متغير خاطئ داخل عامل التشغيل "for". ضع في اعتبارك مراجعة "i". 569
void CTaxFormat::x_LoadTaxTree(void) { .... for(size_t i = 0; i < alignTaxids.size(); i++) { int tax_id = alignTaxids[i]; .... for(size_t j = 0; i < taxInfo.seqInfoList.size(); j++) { SSeqInfo* seqInfo = taxInfo.seqInfoList[j]; seqInfo->taxid = newTaxid; } .... } .... }
أعتقد ، في حالة الحلقة الداخلية ، المتغير الذي حصلت عليه بشكل عشوائي. بدلاً من ذلك ، يجب استخدام المتغير
j .
V535 يتم استخدام المتغير "i" في هذه الحلقة وفي الحلقة الخارجية. تحقق من الخطوط: 302 ، 309. sls_alp.cpp 309
alp::~alp() { .... if(d_alp_states) { for(i=0;i<=d_nalp;i++)
تبدو دورتان متطابقتان متداخلتان ، حيث يتم إعادة تعيين العداد العام أيضًا ، مريبًا للغاية. يجب على المطورين التحقق مما يحدث هنا.
صفيف الفهرسة غير طبيعي
V520 عامل الفاصلة "،" في تعبير فهرس الصفيف "[- i2، - k]". 564
void CSplicedAligner16::x_DoBackTrace ( const Uint2* backtrace_matrix, CNWAligner::SAlignInOut* data, int i_global_max, int j_global_max) { .... while(intron_length < m_IntronMinSize || (Key & donor) == 0) { Key = backtrace_matrix[--i2, --k]; ++intron_length; data->m_transcript.push_back(eTS_Intron); } .... }
يجب أن أقول على الفور أنه يبدو أنه لا يوجد خطأ (الآن ، لول). خذ بعين الاعتبار السطر التالي:
Key = backtrace_matrix[--i2, --k];
قد تشير كلمة "مصفوفة" والفهرسة المزدوجة إلى أن الصفيف ثنائي الأبعاد ، ولكنه ليس كذلك. هذا مؤشر منتظم لمجموعة من الأعداد الصحيحة. لكن تشخيصات
V520 لم تظهر فقط. الخلط بين المبرمجين حول كيفية فهرسة المصفوفات ثنائية الأبعاد.
في هذه الحالة ، قرر المؤلف ببساطة الحفظ في سطر واحد من التعليمات البرمجية ، على الرغم من أنه يمكنه الكتابة على النحو التالي:
--i2; Key = backtrace_matrix[--k];
V661 تعبير مريب 'A [B == C]'. ربما يعني "أ [ب] == ج". 180
static EHTTP_HeaderParse s_ParseHeader(const char* header, ....) { .... if (sscanf(header, "%u.%u.%u.%u%n", &i1, &i2, &i3, &i4, &n) < 4 || sscanf(header + n, "%hu%x%n", &uuu->port, &tkt, &m) < 2 || (header[m += n] && !(header[m] == '$') && !isspace((unsigned char)((header + m) [header[m] == '$'])))) { break; } .... }
مثال آخر على الرمز الذي قضيت فيه وقتًا طويلاً أحاول فيه فهم ما يجري: د. تتحقق الدالة
isspace () من الحرف باستخدام الفهرس
m ، ولكن إذا كان هذا الحرف هو '$' ، فسيتم تمرير الحرف الذي يحمل الفهرس
m + 1 إلى الدالة. علاوة على ذلك ، فإن المقارنة مع "$" كانت مُسبقة بالفعل. ربما لا يوجد خطأ هنا ، ولكن يمكن بالتأكيد إعادة كتابة الرمز بشكل أكثر وضوحًا.
V557 تجاوز الصفيف ممكن. يشير مؤشر "الصف" إلى تجاوز الصفيف. 412
bool CAlnReader::x_IsGap(TNumrow row, TSeqPos pos, const string& residue) { if (m_MiddleSections.size() == 0) { x_CalculateMiddleSections(); } if (row > m_MiddleSections.size()) { return false; } if (pos < m_MiddleSections[row].first) { .... } .... }
هنا يوجد خطأ جسيم. يجب أن يكون الاختيار الصحيح لفهرس
الصف كما يلي:
if (row >= m_MiddleSections.size()) { return false; }
خلاف ذلك ، من الممكن الوصول إلى البيانات خارج ناقل
MiddleSections .
العديد من هذه الأماكن:
- V557 تجاوز الصفيف ممكن. يشير مؤشر "i" إلى تجاوز الصفيف. 388
- V557 تجاوز الصفيف ممكن. يشير مؤشر "الصف" إلى تجاوز الصفيف. 414.العراقي
- V557 تجاوز الصفيف ممكن. يشير مؤشر "fmt_idx" إلى تجاوز الصفيف. 384
- V557 تجاوز الصفيف ممكن. يشير مؤشر "fmt_idx" إلى تجاوز الصفيف. 183- محمد علي
- V557 تجاوز الصفيف ممكن. يشير مؤشر "num" إلى تجاوز الصفيف. نيوكلانوب cpp 13035
كيف تكسب عدم الثقة في الوظائف
V570 تم تخصيص متغير "m_onClickFunction" لنفسه. alngraphic.hpp 103
void SetOnClickFunctionName(string onClickFunction) { m_onClickFunction = m_onClickFunction; }
لا يوجد شيء للتعليق عليه. يمكنك التعاطف فقط مع الشخص الذي نقر على شيء ما ، نقر ، ولكن لم يتغير شيء.
ستنتج حالتان أخريان لتعيين متغيرات لنفسي قائمة:
- V570 تم تخصيص متغير "iter-> level" لنفسه. 189
- V570 تم تخصيص المتغير 'd_elements_values [ind]' لنفسه. 1416
يتم دائمًا إعادة كتابة معلمة
V763 'w1' في نص الوظيفة قبل استخدامها. عوض 5363
يمكن أن تكون الوظيفة التي تتلف فيها الوسيطة مباشرة عند إدخال الوظيفة مضللة للمطورين الذين يستخدمونها. يجب التحقق من الرمز مرتين.
أخطاء تصميم الفئة
V688 تمتلك وسيطة الدالة "m_qsrc" نفس اسم أحد أعضاء الفئة ، مما قد يؤدي إلى حدوث ارتباك. 873
class CElementaryMatching: public CObject { .... ISequenceSource * m_qsrc; .... void x_CreateIndex (ISequenceSource *m_qsrc, EIndexMode index_more, ....); void x_CreateRemapData(ISequenceSource *m_qsrc, EIndexMode mode); void x_LoadRemapData (ISequenceSource *m_qsrc, const string& sdb); .... };
مباشرة 3 وظائف فئة تحتوي على الحجج التي تتوافق أسماؤها مع حقل الفئة. يمكن أن يؤدي هذا إلى أخطاء في الهيئات الوظيفية: قد يعتقد المبرمج أنه يعمل مع أحد أعضاء الفصل ، مما يغير قيمة المتغير المحلي.
V614 تم استخدام متغير "m_BitSet" غير مهيأ. SnpBitAttributes.hpp 187
يعمل أحد
المُنشّئين بقليل مع المتغير
m_BitSet . الحقيقة هي أن المتغير غير مهيأ. يتم استخدام قيمة "القمامة" عند التكرار الأول للحلقة ، وبعد ذلك يحدث التهيئة. هذا خطأ فادح للغاية ، يؤدي إلى سلوك برنامج غير محدد.
V603 تم تكوين العنصر لكن لم يتم استخدامه. إذا كنت ترغب في استدعاء مُنشئ ، يجب استخدام 'this-> SIntervalComparisonResult :: SIntervalComparisonResult (....)'. Compare_feats.hpp 100
منذ فترة طويلة لم أواجه مثل هذه الأخطاء عند التحقق من المشاريع. لكن المشكلة لا تزال ذات صلة. الخطأ هو أن استدعاء المُنشئ ذي المعلمات بهذه الطريقة يُنشئ ويزيل كائنًا مؤقتًا. وتبقى الحقول الصفية غير مهيأة. يجب استدعاء مُنشئ آخر من خلال قائمة التهيئة (انظر
مُنشئ التفويض ).
يجب أن ترجع الدالة
V591 قيمة. علي محمد علي 268
يرى المحلل أن الخط مفقود في العبارة الزائدة:
return *this;
V670 يتم استخدام عضو الفئة غير المسمى 'm_OutBlobIdOrData' لتهيئة عضو 'm_StdOut'. تذكر أنه تتم تهيئة الأعضاء بترتيب إعلاناتهم داخل الفصل الدراسي. ريموت 215
class NCBI_XCONNECT_EXPORT CRemoteAppResult { public: CRemoteAppResult(CNetCacheAPI::TInstance netcache_api, size_t max_inline_size = kMaxBlobInlineSize) : m_NetCacheAPI(netcache_api), m_RetCode(-1), m_StdOut(netcache_api, m_OutBlobIdOrData, m_OutBlobSize), m_OutBlobSize(0), m_StdErr(netcache_api, m_ErrBlobIdOrData, m_ErrBlobSize), m_ErrBlobSize(0), m_StorageType(eBlobStorage), m_MaxInlineSize(max_inline_size) { } .... };
يتم إصدار 3 تحذيرات محلل على الفور إلى جزء التعليمات البرمجية هذا. لا تتم تهيئة حقول الصف بالترتيب الذي تم إدراجه في قائمة التهيئة ، ولكن بالطريقة التي يتم الإعلان عنها في الفصل. السبب الكلاسيكي للخطأ هو أنه ليس كل المبرمجين يتذكرون أو يعرفون هذه القاعدة. هنا وفي قائمة التهيئة فقط ترتيب خاطئ. يشعر المرء أنه تم إدخال قائمة الحقول بترتيب عشوائي.
تشريح الكائن
V746 . يجب أن يتم اكتشاف الاستثناء بالإشارة وليس بالقيمة. الكوبالت cpp 247
void CMultiAligner::SetQueries(const vector< CRef<objects::CBioseq> >& queries) { .... try { seq_loc->SetId(*it->GetSeqId()); } catch (objects::CObjMgrException e) { NCBI_THROW(CMultiAlignerException, eInvalidInput, (string)"Missing seq-id in bioseq. " + e.GetMsg()); } m_tQueries.push_back(seq_loc); .... }
يمكن أن يؤدي التقاط الاستثناءات حسب القيمة إلى فقدان بعض المعلومات حول الاستثناء بسبب إنشاء كائن جديد. من الأفضل والأكثر أمانًا الحصول على استثناء بالإشارة.
أماكن مماثلة:
- تشريح الكائن V746. يجب أن يتم اكتشاف الاستثناء بالإشارة وليس بالقيمة. 562
- تشريح الكائن V746. يجب أن يتم اكتشاف الاستثناء بالإشارة وليس بالقيمة. 320
- تشريح الكائن V746. يجب أن يتم اكتشاف الاستثناء بالإشارة وليس بالقيمة. 458
- تشريح الكائن V746. يجب أن يتم اكتشاف الاستثناء بالإشارة وليس بالقيمة. 691
- تشريح الكائن V746. يجب أن يتم اكتشاف الاستثناء بالإشارة وليس بالقيمة. كوبالت 719
- تشريح الكائن V746. يجب أن يتم اكتشاف الاستثناء بالإشارة وليس بالقيمة. 728
- تشريح الكائن V746. يجب أن يتم اكتشاف الاستثناء بالإشارة وليس بالقيمة. 777
حول التعليمات البرمجية التي لا يمكن الوصول إليها ومشكلات تنفيذ التعليمات البرمجية الأخرى
تم الكشف عن
V779 رمز
يتعذر الوصول إليه. من الممكن أن يكون هناك خطأ. 627
bool CMergeTree::x_FindBefores_Up_Iter(....) { .... FirstFrame->Curr = StartCurr; FirstFrame->Returned = false; FirstFrame->VisitCount = 0; FrameStack.push_back(FirstFrame); while(!FrameStack.empty()) { .... if(Rel == CEquivRange::eAfter) { Frame->Returned = false; FrameStack.pop_back(); continue; } else if(Rel == CEquivRange::eBefore) { .... continue; } else { if(Frame->VisitCount == 0) { .... continue; } else { .... continue; } } Frame->Returned = false;
يتم كتابة رمز البيان الشرطي بحيث تنتهي جميع فروع الكود تمامًا بعبارة المتابعة. وقد أدى ذلك إلى تشكيل عدة أسطر من التعليمات البرمجية التي يتعذر الوصول إليها في
حلقة while . هذه الخطوط تبدو مشبوهة للغاية. على الأرجح ، نشأت هذه المشكلة بعد إعادة هيكلة الكود ، والآن تتطلب مراجعة دقيقة للكود.
V519 يتم تعيين قيم المتغير "
interal_width " مرتين على التوالي. ربما هذا خطأ. خطوط التحقق: 454 ، 456. aln_writer.cpp 456
void CAlnWriter::AddGaps(....) { .... switch(exon_chunk->Which()) { case CSpliced_exon_chunk::e_Match: interval_width = exon_chunk->GetMatch(); case CSpliced_exon_chunk::e_Mismatch: interval_width = exon_chunk->GetMismatch(); case CSpliced_exon_chunk::e_Diag: interval_width = exon_chunk->GetDiag(); genomic_string.append(....); product_string.append(....); genomic_pos += interval_width; product_pos += interval_width/res_width; break; .... } .... }
تم استبدال
الفاصل الزمني المتغير عدة مرات ، لأنه لا توجد عبارات
استراحة في
حالة الفروع. على الرغم من أنه خطأ كلاسيكي ، ولكنه خطأ سيئ للغاية.
بعض الأماكن المريبة:
- تم الكشف عن V779 رمز يتعذر الوصول إليه. من الممكن أن يكون هناك خطأ. 351
- تم الكشف عن V779 رمز يتعذر الوصول إليه. من الممكن أن يكون هناك خطأ. نت.ج 780
- تم الكشف عن V779 رمز يتعذر الوصول إليه. من الممكن أن يكون هناك خطأ. قبل الميلاد ج 1495
- تم الكشف عن V779 رمز يتعذر الوصول إليه. من الممكن أن يكون هناك خطأ. 1470
- تم الكشف عن V779 رمز يتعذر الوصول إليه. من الممكن أن يكون هناك خطأ. 1522
الشيكات المتكررة
V571 . تم التحقق من حالة "if (m_QueryOpts-> filtering_options)" بالفعل في السطر 703. blast_options_local_priv.hpp 713
inline void CBlastOptionsLocal::SetFilterString(const char* f) { .... if (m_QueryOpts->filtering_options)
من الواضح أن الفرع
الآخر يتطلب إعادة الكتابة. لدي بعض الأفكار التي أردت القيام بها
بمؤشر m_QueryOpts-> filtering_options ، لكن الشفرة لا تزال مربكة إلى حد ما. أناشد أصحاب الكود.
حسنًا ، المشكلة لا تأتي وحدها:
- الشيكات المتكررة V571. تم التحقق من حالة "if (sleeptime)" بالفعل في السطر 205. request_control.cpp 208
- الشيكات المتكررة V571. تم التحقق من شرط "if (assignValue.empty ())" بالفعل في السطر 712. classstr.cpp 718
أخطاء قراءة البيانات
لا يجب مقارنة
V739 EOF بقيمة من النوع 'char'. يجب أن يكون "linestring [0]" من النوع "int". النيد. c 3509
static EBool s_AfrpInitLineData( .... char* linestring = readfunc (pfile); .... while (linestring != NULL && linestring [0] != EOF) { s_TrimSpace (&linestring); .... } .... }
لا يجب تخزين الأحرف التي تخطط لمقارنتها مع EOF في متغيرات الأحرف. خلاف ذلك ، هناك خطر من أن يتحول حرف بقيمة 0xFF (255) إلى -1 وسيتم تفسيره بنفس طريقة نهاية الملف (EOF). أيضا (فقط في حالة) يجدر التحقق من تنفيذ وظيفة
readfunc .
حلقة
V663 اللانهائية ممكنة. شرط 'cin.eof () غير كافٍ للكسر من الحلقة.
ضع في اعتبارك إضافة استدعاء دالة "cin.fail ()" إلى التعبير الشرطي. ncbicgi.cpp 1564 typedef std::istream CNcbiIstream; void CCgiRequest::Serialize(CNcbiOstream& os) const { .... CNcbiIstream* istrm = GetInputStream(); if (istrm) { char buf[1024]; while(!istrm->eof()) { istrm->read(buf, sizeof(buf)); os.write(buf, istrm->gcount()); } } }
اكتشف المحلل وجود خطأ محتمل بسبب حدوث حلقة لانهائية. إذا حدث فشل أثناء قراءة البيانات ، فسيؤدي استدعاء الدالة eof () إلى إرجاع خطأ دائمًا . لإكمال الحلقة في هذه الحالة ، من الضروري إجراء فحص إضافي للقيمة التي يتم إرجاعها بواسطة الدالة fail () .أخطاء متنوعة
V502 ربما يكون عامل التشغيل '؟:' يعمل بطريقة مختلفة عما كان متوقعًا. عامل التشغيل '؟:' له أولوية أقل من عامل التشغيل "&&". 1135 static const char* x_ClientAddress(const char* client_host, int local_host) { .... if ((client_host == c && x_IsSufficientAddress(client_host)) || !(ip = *c && !local_host ? SOCK_gethostbyname(c) : SOCK_GetLocalHostAddress(eDefault)) || SOCK_ntoa(ip, addr, sizeof(addr)) != 0 || !(s = (char*) malloc(strlen(client_host) + strlen(addr) + 3))) { return client_host; } .... }
انتبه للتعبير: !local_host ? SOCK_gethostbyname(c) : SOCK_GetLocalHostAddress(eDefault)
لا يتم حسابه كما توقع المبرمج ، لأن التعبير بأكمله يبدو كما يلي: ip = *c && !local_host ? SOCK_gethostbyname(c) : SOCK_GetLocalHostAddress(...)
أولوية && عامل التشغيل أعلى من ؟: . لهذا السبب ، لا يتم تنفيذ التعليمات البرمجية على النحو المنشود.V561 ربما يكون من الأفضل تعيين قيمة للمتغير "seq" بدلاً من إعلانها من جديد. الإعلان السابق: validator.cpp ، السطر 490. validator.cpp 492 bool CValidator::IsSeqLocCorrectlyOrdered(const CSeq_loc& loc, CScope& scope) { CBioseq_Handle seq; try { CBioseq_Handle seq = scope.GetBioseqHandle(loc); } catch (CObjMgrException& ) {
بسبب إعلان المبرمج عن متغير seq جديد داخل قسم المحاولة / الالتقاط ، يظل المتغير seq الآخر غير مهيأ ويستخدم أدناه في الكود.V562 من الغريب مقارنة قيمة نوع منطقي بقيمة 0: (((status) & 0x7f) == 0)! = 0. ncbi_process.cpp 111 bool CProcess::CExitInfo::IsExited(void) const { EXIT_INFO_CHECK; if (state != eExitInfo_Terminated) { return false; } #if defined(NCBI_OS_UNIX) return WIFEXITED(status) != 0; #elif defined(NCBI_OS_MSWIN)
لا شيء يبشر بالمرض ، ولكن تبين أن WIFEXITED فتح ماكرو بهذه الطريقة: return (((status) & 0x7f) == 0) != 0;
اتضح أن الدالة ترجع القيمة المعاكسة.في الكود ، كانت هناك وظيفة أخرى من هذا القبيل ، أصدرت تحذيرًا:- V562 من الغريب مقارنة قيمة نوع منطقي بقيمة 0. ncbi_process.cpp 126
V595 تم استخدام مؤشر "dst_len" قبل التحقق منه من خلال nullptr. خطوط التحقق: 309 ، 315. zlib.cpp 309 bool CZipCompression::CompressBuffer( const void* src_buf, size_t src_len, void* dst_buf, size_t dst_size, size_t* dst_len) { *dst_len = 0;
يتم إلغاء الإشارة إلى مؤشر dst_len في بداية الوظيفة ، بينما يتم التحقق من وجود رمز إضافي يساوي صفر. في التعليمات البرمجية، قمت بخطأ ما، الأمر الذي يؤدي إلى سلوك غير معرف، وإذا كان مؤشر dst_len سيكون مساويا ل nullptr .V590 خذ بعين الاعتبار فحص التعبير 'ch! =' \ 0 '&& ch ==' ''. التعبير زائد أو يحتوي على خطأ مطبعي. 580 bool Asn2gnbkCompressSpaces(string& val) { .... while (ch != '\0' && ch == ' ') { ptr++; ch = *ptr; } .... }
تعتمد حالة إيقاف الحلقة فقط على ما إذا كان الحرف ch مسافة أم لا. يمكن تبسيط التعبير لما يلي: while (ch == ' ') { .... }
الخلاصة
إن استخدام برامج الكمبيوتر في البحث العلمي يساعد وسيساعد على تحقيق الاكتشافات. دعونا نأمل ألا يتم تفويت تلك المهمة بشكل خاص بسبب بعض الأخطاء المطبعية.أدعو مطوري مشروع NCBI Genome Workbench للاتصال بنا ، وسنقدم تقريرًا كاملاً صادرًا عن محلل PVS-Studio.نأمل أن يساعد هذا البحث الصغير في التعليمات البرمجية في إصلاح العديد من الأخطاء وتحسين موثوقية المشروع بشكل عام. حاول تشغيل برنامج PVS-Studio وفقًا لرموز مشاريعك ، إذا لم تكن قد قمت بذلك بالفعل. قد ترغب في ذلك :).
إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فيرجى استخدام رابط الترجمة: Svyatoslav Razmyslov. منضدة عمل جينوم NCBI: البحث العلمي تحت التهديد