
يتزايد عدد قرائنا ، لذلك نكتب مقالات مرارًا وتكرارًا تشرح كيفية استخدام منهجية تحليل التعليمات البرمجية الثابتة بشكل صحيح. نحن نعتبر أنه من المهم للغاية توضيح أن أدوات التحليل الثابت لا ينبغي أن تستخدم بشكل متقطع ، ولكن بانتظام. مرة أخرى ، نوضح هذا بمثال عملي ، إعادة فحص مشروع Godot.
استخدم المحللون بانتظام
أثناء الاستعداد للتحدث في مؤتمر مطوري الألعاب ، قررت الحصول على أمثلة جديدة للأخطاء المثيرة للاهتمام التي يمكن لأداة
PVS-Studio اكتشافها. لهذا الغرض ، تم اختبار العديد من محركات الألعاب ، أحدها Godot. لم أجد أي أخطاء مثيرة للاهتمام بشكل خاص للتقرير ، لكني أردت كتابة مقال حول الأخطاء العادية. توضح هذه الأخطاء جيدًا أهمية الاستخدام المنتظم لأدوات تحليل التعليمات البرمجية الثابتة.
وتجدر الإشارة إلى أننا
فحصنا بالفعل هذا المشروع في عام 2015 ، وعمل المؤلف مع الأخطاء التي كتبناها.
هنا يمكنك رؤية الالتزام المقابل.
لقد مرت 3 سنوات. لقد تغير المشروع. كما تغير محلل PVS-Studio ، وظهرت فيه تشخيصات جديدة. لذلك ، ليس من المستغرب أن أتمكن من كتابة أمثلة كافية على الأخطاء بسهولة وسرعة لكتابة هذه المقالة.
ومع ذلك ، هناك شيء آخر مهم. عند تطوير Godot أو أي مشروع آخر ، تظهر أخطاء جديدة باستمرار ويتم إصلاحها. الأخطاء غير المكتشفة "تستقر" في الكود لفترة طويلة ، وبعد ذلك يمكن اكتشاف الكثير منها عند تشغيل تحليل الكود الثابت. وبسبب هذا ، هناك أحيانًا شعور خاطئ بأن المحللين الساكنين لا يجدون سوى بعض الأخطاء غير المثيرة للاهتمام في أقسام من التعليمات البرمجية نادرًا ما تستخدم. بالطبع ، هذا هو الحال إذا كنت تستخدم المحلل بشكل غير صحيح وقمت بتشغيله فقط من وقت لآخر ، على سبيل المثال ، قبل وقت قصير من إصدار الإصدار.
نعم ، عند كتابة المقالات ، نقوم بأنفسنا بإجراء فحوصات لمرة واحدة للمشاريع المفتوحة. لكن لدينا هدف مختلف. نريد أن نثبت قدرات محلل الكود لاكتشاف العيوب. هذه المهمة لا علاقة لها بتحسين جودة رمز المشروع ككل وخفض التكاليف المرتبطة بتصحيح الأخطاء.
مرة أخرى حول الشيء الرئيسي. ليس الهدف من تحليل الرمز الثابت هو العثور على أخطاء قديمة. عادة ما تكون هذه الأخطاء القديمة غير ذات أهمية ، وإلا فإنها قد تتداخل مع المستخدمين وكان يمكن العثور عليها بالفعل وإصلاحها. الهدف من التحليل الثابت هو إصلاح الأخطاء بسرعة في التعليمات البرمجية المكتوبة أو المعدلة حديثًا. هذا يقلل من وقت التصحيح ، وعدد شكاوى المستخدمين ، وفي النهاية ، يقلل من ميزانية المشروع المطور.
الآن دعنا ننتقل إلى الأخطاء التي يحبها قراء منشوراتنا كثيرًا.
أخطاء نسخ ولصق
دعونا نرى ما لاحظته أثناء دراسة تقرير PVS-Studio. سأبدأ بتشخيصي المفضل V501 ، الذي يجد
أخطاء في كل مشروع نتحقق منه تقريبًا :).
خطأ N1virtual bool can_export(....) { .... if (!exists_export_template("uwp_" + platform_infix + "_debug.zip", &err) || !exists_export_template("uwp_" + platform_infix + "_debug.zip", &err)) { valid = false; r_missing_templates = true; } .... }
تحذير PVS-Studio: V501 CWE-570 هناك تعبيرات فرعية متطابقة! Exists_export_template ("uwp_" + platform_infix + "_debug.zip" ، وخطأ) "إلى اليسار وإلى يمين" || " عامل. تصدير. cpp 1135
خطأ النسخ واللصق الكلاسيكي. تم نسخ استدعاء الوظيفة ولكن لم يتم تحريره. يجب أن ينتهي اسم الملف الثاني المراد معالجته بـ "_release.zip".
الأخطاء N2 و N3 static String dump_node_code(SL::Node *p_node, int p_level) { .... if (bnode->statements[i]->type == SL::Node::TYPE_CONTROL_FLOW || bnode->statements[i]->type == SL::Node::TYPE_CONTROL_FLOW) { code += scode;
تحذير PVS-Studio: V501 CWE-570 هناك عبارات فرعية متطابقة 'bnode-> عبارات [i] -> type == SL :: Node :: TYPE_CONTROL_FLOW' إلى اليسار وإلى يمين '||' عامل. محمد عبدالله محمد الجابري 183
void EditorSpinSlider::_notification(int p_what) { if (p_what == MainLoop::NOTIFICATION_WM_FOCUS_OUT || p_what == MainLoop::NOTIFICATION_WM_FOCUS_OUT) { if (grabbing_spinner) { Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); grabbing_spinner = false; grabbing_spinner_attempt = false; } } .... }
تحذير PVS-Studio: V501 CWE-570 هناك تعبيرات فرعية متطابقة 'p_what == MainLoop :: NOTIFICATION_WM_FOCUS_OUT' إلى يسار ويمين '||' عامل. editor_spin_slider.cpp 157
أعتقد أن الأخطاء مرئية بوضوح ولا تتطلب أي تفسير. تمامًا نفس النسخ واللصق الكلاسيكي ، كما هو الحال في الحالة الأولى.
خطأ N4 String SoftBody::get_configuration_warning() const { .... Transform t = get_transform(); if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(0).length() - 1.0) > 0.05)) { if (!warning.empty()) .... }
تحذير PVS-Studio: V501 CWE-570 هناك تعبيرات فرعية متطابقة إلى اليسار وإلى اليمين من "||" عامل. 399
هنا تم نسخ السطر الأول مرتين. ولكن تم تغيير رقم محور الإحداثيات فقط في السطر الثاني. وقد نسوا تحرير السطر الثالث. هذا ليس سوى "
تأثير الخط الأخير ".
ملاحظة في الوقت الحالي ، بالإضافة إلى "تأثير السطر الأخير" ، قمت بالملاحظات المثيرة للاهتمام التالية: "
الوظيفة الأكثر خطورة في عالم C / C ++ " ، "
يعيش الشر في وظائف المقارنة ". والآن سأعلن عن مقال جديد ، أخطط لكتابته في المستقبل القريب. عنوان العمل هو "0 ، 1 ، 2". يجب أن تكون مثيرة للاهتمام ومفيدة. أدعوكم للاشتراك في إحدى القنوات حتى لا تفوتكم:
twitter و
vk.com و
Instagram و
telegram و "old school"
rss .
خطأ N5 void ScrollContainer::_notification(int p_what) { .... if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) size.y -= h_scroll->get_minimum_size().y; if (v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) size.x -= h_scroll->get_minimum_size().x; .... }
تحذير PVS-Studio: تم العثور على V778 CWE-682 جزأين من التعليمات البرمجية المماثلة. ربما ، هذا هو خطأ مطبعي ويجب استخدام متغير "v_scroll" بدلاً من "h_scroll". 249 /
فيما يتعلق بهذا الجزء من التعليمات البرمجية ، لست متأكدًا تمامًا من وجود خطأ. ومع ذلك ، أتفق مع المحلل أن الكتلة الثانية تبدو مريبة للغاية. على الأرجح ، تم كتابة الرمز باستخدام Copy-Paste ، وفي المجموعة الثانية من النص نسوا استبدال
h_scroll بـ
v_scroll .
ربما يجب أن يكون الرمز على النحو التالي:
if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) size.y -= h_scroll->get_minimum_size().y; if (v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) size.x -= v_scroll->get_minimum_size().x;
خطأ N6حالة أخرى تم فيها نسخ جزء رمز كبير بما فيه الكفاية وتغييرها دون جدوى. خط الخطأ يتميز بتعليقي "// <=".
void ShaderGLES2::bind_uniforms() { .... const Map<uint32_t, Variant>::Element *E = uniform_defaults.front(); while (E) { int idx = E->key(); int location = version->uniform_location[idx]; if (location < 0) { E = E->next(); continue; } Variant v; v = E->value(); _set_uniform_variant(location, v); E = E->next(); } const Map<uint32_t, CameraMatrix>::Element *C = uniform_cameras.front(); while (C) { int idx = E->key();
تحذير PVS-Studio: V522 CWE-476 قد يحدث إلغاء إشارة للمؤشر الفارغ "E". شادي العبيدي 102
تم الكشف عن الخطأ بشكل غير مباشر. بفضل تحليل
تدفق البيانات ، كشف PVS-Studio أن المؤشر
E قد يكون صفرًا في وقت إلغاء الإشارة.
الخطأ هو أنه في جزء الشفرة المنسوخة نسوا استبدال
E في
C في مكان واحد. بسبب هذا الخطأ ، تعمل الوظيفة بطريقة غريبة جدًا وتقوم بأشياء غريبة.
الأخطاء المطبعية
خطأ N7من الصعب على المبرمجين الذين يكتبون بلغات أخرى غير C أو C ++ أن يتصوروا أنه يمكن عمل خطأ مطبعي عن طريق كتابة فاصلة بدلاً من علامة النجمة * ، وسيتم تجميع الرمز. ومع ذلك ، هذا صحيح.
LRESULT OS_Windows::WndProc(....) { .... BITMAPINFO bmi; ZeroMemory(&bmi, sizeof(BITMAPINFO)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = dib_size.x; bmi.bmiHeader.biHeight = dib_size.y; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biSizeImage = dib_size.x, dib_size.y * 4; .... }
تحذير PVS-Studio: V521 CWE-480 مثل هذه التعبيرات التي تستخدم عامل التشغيل "،" خطيرة. تأكد من صحة التعبير. 776
يتم تعيين قيمة المتغير
bmi.bmiHeader.biSizeImage قيمة المتغير
dib_size.x . بعد ذلك ، يتم تنفيذ عامل الفاصلة '،' ، حيث تكون
أولويته أقل من
أولوية عامل التشغيل '='. لا يتم استخدام نتيجة التعبير
dib_size.y * 4 بأي شكل من الأشكال.
بدلاً من الفاصلة في التعبير ، يجب استخدام عامل الضرب '*'. أولاً ، مثل هذا التعبير سيكون منطقيًا. ثانيًا ، يوجد أدناه خيار مشابه ، ولكنه صحيح بالفعل لتهيئة المتغير نفسه:
bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
الأخطاء N8 و N9 void Variant::set(....) { .... int idx = p_index; if (idx < 0) idx += 4; if (idx >= 0 || idx < 4) { Color *v = reinterpret_cast<Color *>(_data._mem); (*v)[idx] = p_value; valid = true; } .... }
تحذير PVS-Studio: V547 CWE-571 Expression 'idx> = 0 || idx <4 'صحيح دائمًا. 2152
سيتم اعتبار أي فهرس صحيح. لإصلاح الخطأ ، تحتاج إلى استبدال
|| على
&& :
if (idx >= 0 && idx < 4) {
هذا الخطأ المنطقي نشأ على الأرجح بسبب الإهمال ، لذلك أميل إلى نسبه إلى الأخطاء المطبعية.
يمكن ملاحظة نفس الخطأ بالضبط في نفس الملف أدناه. خطأ خطأ التكاثر ، على ما يبدو ، هو نسخ ولصق.
أخطاء متعددة: V547 CWE-571 Expression 'idx> = 0 || idx <4 'صحيح دائمًا. 2527
خطأ N10مثال على خطأ يريد المرء أن يصيح من: WTF؟!
void AnimationNodeBlendSpace1D::add_blend_point( const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index) { ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); ERR_FAIL_COND(p_node.is_null()); ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used); if (p_at_index == -1 || p_at_index == blend_points_used) { p_at_index = blend_points_used; } else { for (int i = blend_points_used - 1; i > p_at_index; i++) { blend_points[i] = blend_points[i - 1]; } } .... }
تحذير PVS-Studio: V621 CWE-835 فكر في فحص عامل التشغيل "for". من الممكن أن يتم تنفيذ الحلقة بشكل غير صحيح أو لن يتم تنفيذها على الإطلاق. 113- الرسم المتحرك 113
لاحظ حالة توقف الحلقة:
i> p_at_index . هذا صحيح دائمًا ، نظرًا لأن المتغير
i تمت تهيئته بالقيمة
blend_points_used - 1 . علاوة على ذلك ، من
فحصين سابقين ، يتبع ذلك
blend_points_used> p_at_index .
يمكن أن تصبح الحالة خاطئة فقط عندما يحدث تجاوز لمتغير الإشارة
i ، وهو سلوك غير محدد. علاوة على ذلك ، لن تصل إلى تجاوز ، لأن الصفيف سيتجاوز الحدود في وقت أبكر بكثير.
أمامنا ، في رأيي ، خطأ مطبعي جميل عندما كتبوا ">" بدلاً من "<". نعم لدي نظرة منحرفة لجمال الأخطاء :).
الدورة الصحيحة:
for (int i = blend_points_used - 1; i < p_at_index; i++) {
خطأ N11حالة أخرى من الأخطاء المطبعية في حالة الدورة.
void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { .... int idx = -1; for (int i = 0; node_rects.size(); i++) { if (node_rects[i].node_name == playback->get_current_node()) { idx = i; break; } } .... }
تحذير PVS-Studio: V693 CWE-835 ضع في اعتبارك فحص التعبير الشرطي للحلقة. من الممكن استخدام "i <X.size ()" بدلاً من "X.size ()". 852
قد يحدث تجاوز للصفيف ، حيث تزيد قيمة
i بشكل لا يمكن السيطرة عليه. الكود الآمن:
for (int i = 0; i < node_rects.size(); i++) {
خطأ N12 GDScriptDataType GDScriptCompiler::_gdtype_from_datatype( const GDScriptParser::DataType &p_datatype) const { .... switch (p_datatype.kind) { .... case GDScriptParser::DataType::NATIVE: { result.kind = GDScriptDataType::NATIVE; result.native_type = p_datatype.native_type; } break; case GDScriptParser::DataType::SCRIPT: { result.kind = GDScriptDataType::SCRIPT; result.script_type = p_datatype.script_type; result.native_type = result.script_type->get_instance_base_type(); } case GDScriptParser::DataType::GDSCRIPT: { result.kind = GDScriptDataType::GDSCRIPT; result.script_type = p_datatype.script_type; result.native_type = result.script_type->get_instance_base_type(); } break; .... }
تحذير PVS-Studio: V796 CWE-484 من المحتمل أن تكون عبارة "فاصل" مفقودة في عبارة التبديل. 135
نسيت بطريق الخطأ كتابة بيان
استراحة . لذلك ، عندما تدخل في
الحالة GDScriptParser :: DataType :: SCRIPT ، سيتم تعيين القيم للمتغيرات ، كما لو كانت
الحالة GDScriptParser :: DataType :: GDSCRIPT .
خطأ N13يمكن تصنيف الخطأ التالي كنسخ ولصق. ومع ذلك ، لست متأكدًا مما إذا تم نسخ مثل هذا الخط القصير. لذلك سنعتبر هذا خطأ مطبعيًا بسيطًا عند الكتابة.
void CPUParticles::_particles_process(float p_delta) { .... if (flags[FLAG_DISABLE_Z]) { p.velocity.z = 0.0; p.velocity.z = 0.0; } .... }
تحذير PVS-Studio: V519 CWE-563 يتم تعيين قيم المتغير "p.velocity.z" مرتين على التوالي. ربما هذا خطأ. خطوط التحقق: 664 ، 665. cpu_particles.cpp 665
مرتين تعيين نفس المتغير. يمكنك أن ترى أدناه جزء التعليمات البرمجية التالي:
if (flags[FLAG_DISABLE_Z]) { p.velocity.z = 0.0; p.transform.origin.z = 0.0; }
على الأرجح ، في الحالة الأولى كان يجب أن تتم كتابتها بنفس الطريقة.
خطأ N14 bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const { if (atlas.is_valid()) { return atlas->is_pixel_opaque( p_x + region.position.x + margin.position.x, p_x + region.position.y + margin.position.y ); } return true; }
تحذير PVS-Studio: لا يتم استخدام المعلمة V751 "p_y" داخل نص الوظيفة. نسيج cpp 1085
جزء من وصف التشخيص
V751 :
اكتشف المحلل وظيفة مشبوهة ، لم يتم استخدام أحد معلماتها أبدًا. في نفس الوقت ، يتم استخدام المعلمة الأخرى عدة مرات ، مما يشير على الأرجح إلى وجود خطأ.كما ترون ، هذا بالفعل ، وهو مشبوه للغاية.
يتم استخدام المتغير
p_x مرتين ، ولا يستخدم
p_y . على الأرجح ، يجب كتابتها:
return atlas->is_pixel_opaque( p_x + region.position.x + margin.position.x, p_y + region.position.y + margin.position.y );
بالمناسبة ، في رمز المصدر ، تتم كتابة استدعاء الوظيفة في سطر واحد. وبسبب هذا ، يصعب ملاحظة الخطأ. إذا كتب مؤلف الشفرة الحجج الفعلية في عمود ، كما فعلت في المقالة ، فإن الخطأ سيلفت انتباهي على الفور. تذكر أن تنسيق الجدول مفيد للغاية ويتجنب الكثير من الأخطاء المطبعية. راجع فصل "محاذاة نفس النوع من التعليمات البرمجية مع" جدول "في مقالة"
القضية الرئيسية للبرمجة وإعادة الهيكلة وكل ذلك . "
خطأ N15 bool SpriteFramesEditor::can_drop_data_fw(....) const { .... Vector<String> files = d["files"]; if (files.size() == 0) return false; for (int i = 0; i < files.size(); i++) { String file = files[0]; String ftype = EditorFileSystem::get_singleton()->get_file_type(file); if (!ClassDB::is_parent_class(ftype, "Texture")) { return false; } } .... }
تحذير PVS-Studio: V767 وصول مشبوه إلى عنصر صفيف 'ملفات' بواسطة فهرس ثابت داخل حلقة. 602
تقوم الحلقة بمعالجة نفس الملف في جميع تكرارات الحلقة. خطأ مطبعي هنا:
String file = files[0];
يجب أن يكون:
String file = files[i];
أخطاء أخرى
خطأ N16 CSGBrush *CSGBox::_build_brush() { .... for (int i = 0; i < 6; i++) { .... if (i < 3) face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); else face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); .... } .... }
ينتج محلل PVS-Studio على الفور ردين على هذا الرمز:
- V547 CWE-570 التعبير 'i> = 3' خطأ دائمًا. 939
- V547 CWE-571 التعبير "i> = 3" صحيح دائمًا. csg_shape.cpp 941
في الواقع ، يبدو هذا العامل الثلاثي في كلا التعبيرين غريبًا جدًا:
i >= 3 ? -1 : 1
في إحدى الحالات ، يكون الشرط دائمًا صحيحًا ، وفي الحالة الأخرى يكون خطأ. من الصعب القول كيف يجب أن يبدو هذا الرمز صحيحًا. ربما تكون زائدة عن الحاجة ويمكن كتابتها على النحو التالي:
for (int i = 0; i < 6; i++) { .... if (i < 3) face_points[j][(i + k) % 3] = v[k]; else face_points[3 - j][(i + k) % 3] = -v[k]; .... }
قد أكون مخطئًا ، ويجب إصلاح الرمز بطريقة مختلفة تمامًا.
خطأ N17لم تكن هناك أخطاء تقريبًا مثل V595 ، على الرغم من وجودها عادةً
بوفرة في أي مشروع . على ما يبدو ، تم إصلاح هذه الأخطاء بعد فحص سابق ، ومن ثم لم تظهر أخطاء من هذا النوع تقريبًا. رأيت فقط بعض الإيجابيات الخاطئة وخطأ واحد.
bool CanvasItemEditor::_get_bone_shape(....) { .... Node2D *from_node = Object::cast_to<Node2D>( ObjectDB::get_instance(bone->key().from)); .... if (!from_node->is_inside_tree()) return false;
تحذير PVS-Studio: V595 CWE-476 تم استخدام مؤشر "from_node" قبل التحقق من صحته باستخدام nullptr. خطوط التحقق: 565 ، 567. canvas_item_editor_plugin.cpp 565
يتم
إلغاء الإشارة إلى المؤشر
from_node أولاً لاستدعاء الدالة
is_inside_tree ، وعندئذٍ فقط يتم التحقق من
وجود المساواة في
nullptr . يجب تبديل الشيكات:
if (!from_node) return false; if (!from_node->is_inside_tree()) return false;
خطأ N18 enum JoystickList { .... JOY_AXIS_MAX = 10, .... }; static const char *_axes[] = { "Left Stick X", "Left Stick Y", "Right Stick X", "Right Stick Y", "", "", "L2", "R2" }; int InputDefault::get_joy_axis_index_from_string(String p_axis) { for (int i = 0; i < JOY_AXIS_MAX; i++) { if (p_axis == _axes[i]) { return i; } } ERR_FAIL_V(-1); }
تحذير PVS-Studio: يمكن تجاوز صفيف V557 CWE-125. يمكن أن تصل قيمة مؤشر "i" إلى 9. input_default.cpp 1119
تتكون مجموعة
_axes من ثمانية عناصر. في هذه الحالة ، يكون ثابت
JOY_AXIS_MAX ، الذي يحدد عدد التكرارات للحلقة ، هو 10. وتبين أن الحلقة تتجاوز حدود الصفيف.
خطأ N19وآخر وظيفة غريبة للغاية ، والتي يبدو أنها مصممة لاختبار شيء ما. الوظيفة طويلة ، لذلك سأعطيها كصورة (انقر على الصورة للتكبير).
تحذير PVS-Studio: تم الكشف عن V779 CWE-561 رمز لا يمكن الوصول إليه. من الممكن أن يكون هناك خطأ. 457
هناك العديد من
عبارات الإرجاع غير المشروطة في دالة. في الصورة ، قمت بوضع علامة عليها بالأشكال البيضاوية الحمراء. يبدو أنه تم جمع العديد من اختبارات الوحدات المختلفة لهذه الوظيفة ، ولكن نسيت إزالة
القيم الإضافية
العائدة NULLs . ونتيجة لذلك ، لا تتحقق الوظيفة مما ينبغي فحصه. يتكون نص الدالة بالكامل تقريبًا من رمز يتعذر الوصول إليه.
ربما ، بالطبع ، هذه فكرة ماكرة. ولكن يبدو لي أن هذا حدث بالصدفة ويجب إصلاح الشفرة.
دعونا ننتهي من هذا. بالتأكيد ، إذا نظرت عن كثب إلى تقرير المحلل ، يمكنك العثور على أخطاء أخرى. ومع ذلك ، حتى كتابة أكثر من كافية لكتابة مقال. ثم سيصبح مملاً بالنسبة لي والقارئ :).
الخاتمة
تصف المقالة الأخطاء التي لن تكون موجودة إذا تم تحليل الرمز بانتظام باستخدام PVS-Studio. ومع ذلك ، الأهم من ذلك ، باستخدام التحليل المنتظم ، يمكن للمرء أن يجد ويزيل العديد من الأخطاء الأخرى على الفور. وصف زميلي هذه الفكرة بمزيد من التفصيل في ملاحظته: "
فلسفة تحليل الشفرة الثابتة: لدينا 100 مبرمج ، وجد المحلل أخطاء قليلة ، فهل هي عديمة الفائدة؟ ". أوصي بشدة بقضاء 10 دقائق في قراءة هذه المقالة القصيرة ولكنها مهمة جدًا.
شكرا لكم على اهتمامكم. أدعو الجميع لتنزيل ومحاولة التحليل الثابت لـ PVS-Studio لاختبار مشاريعك الخاصة.

إذا كنت تريد مشاركة هذه المقالة مع جمهور ناطق باللغة الإنجليزية ، فيرجى استخدام رابط الترجمة: Andrey Karpov.
جودو: حول الاستخدام المنتظم للمحللات الساكنة .