أفضل خوارزميات نسخ ولصق لـ C و C ++. Haiku OS وصفة مجموعة

أصبحت العديد من الأخطاء المطبعية ورمز نسخ اللصق موضوعًا لمقال إضافي حول التحقق من كود هايكو بواسطة محلل PVS-Studio. ومع ذلك ، ستكون هناك أخطاء مرتبطة ليس كثيرا بالأخطاء المطبعية ، ولكن بالأخطاء والإهمال الناجح لإعادة البناء. توضح أمثلة الأخطاء التي تم العثور عليها مدى قوة العامل البشري في تطوير البرمجيات.

الصورة 1

مقدمة


Haiku هو نظام تشغيل مجاني مفتوح المصدر لأجهزة الكمبيوتر الشخصية. حاليًا ، تعمل مجموعة دولية من المطورين بنشاط على مكونات النظام. من بين آخر التطورات الهامة في التطوير ، تم نقل LibreOffice إلى نظام التشغيل وإصدار أول إصدار تجريبي من R1 Beta 1.

يراقب فريق من مطوري محلل الكود الثابت في PVS-Studio تطور المشروع منذ عام 2015 ، حيث يصدرون مراجعات عن عيوب الكود. هذا هو الاستعراض الرابع في كل العصور. يمكنك التعرف على المقالات السابقة على هذه الروابط:

  1. التحقق من نظام التشغيل هايكو (عائلة BeOS). الجزء 1
  2. التحقق من نظام التشغيل هايكو (عائلة BeOS). الجزء 2
  3. كيف تطلق النار على قدمك في C و C ++. Haiku OS وصفة مجموعة

تتمثل ميزة أحدث تحليل للكود في القدرة على استخدام الإصدار الرسمي من PVS-Studio لنظام التشغيل Linux. في عام 2015 ، لم يكن هناك ، وكذلك تقرير مناسب لعرض الأخطاء. الآن سوف نرسل للمطورين تقريرًا كاملاً بتنسيق مناسب.

كلاسيكيات هذا النوع


V501 هناك تعابير فرعية مماثلة إلى اليسار وإلى يمين عامل التشغيل '-': (addr_t) b - (addr_t) b BitmapManager.cpp 51

int compare_app_pointer(const ServerApp* a, const ServerApp* b) { return (addr_t)b - (addr_t)b; } 

يجب على كل مبرمج في حياته مزج المتغيرات a و b و x و y و i و j ... إلخ.

V501 هناك تعبيرات فرعية مماثلة إلى اليسار وإلى يمين "||" عامل التشغيل: الإدخال == __null || إدخال == __null MediaClient.cpp 182

 status_t BMediaClient::Unbind(BMediaInput* input, BMediaOutput* output) { CALLED(); if (input == NULL || input == NULL) return B_ERROR; if (input->fOwner != this || output->fOwner != this) return B_ERROR; input->fBind = NULL; output->fBind = NULL; return B_OK; } 

في الحالة ، يتم فحص مؤشر الإدخال نفسه مرتين ، ويظل مؤشر الإخراج بدون تحديد ، مما قد يؤدي إلى إلغاء تحديد مؤشر القيمة الخالية.

كود مصحح:

 if (input == NULL || output == NULL) return B_ERROR; 

V583 يقوم المشغل '؟:' ، بغض النظر عن تعبيره الشرطي ، دائمًا بإرجاع قيمة واحدة ونفس القيمة: 500000. usb_modeswitch.cpp 361

 static status_t my_transfer_data(....) { .... do { bigtime_t timeout = directionIn ? 500000 : 500000; result = acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, timeout); .... } while (result == B_INTERRUPTED); .... } 

فقد المشغل الثلاثي معناها عندما ارتكب المبرمج خطأ وكتب قيمتي عائد متطابقتين - 500000 .

V519 يتم تعيين قيم "m_kindex1" لقيمتين متتاليتين. ربما هذا خطأ. خطوط التحقق: 40 ، 41. agg_trans_double_path.cpp 41

 trans_double_path::trans_double_path() : m_kindex1(0.0), m_kindex2(0.0), m_base_length(0.0), m_base_height(1.0), m_status1(initial), m_status2(initial), m_preserve_x_scale(true) { } void trans_double_path::reset() { m_src_vertices1.remove_all(); m_src_vertices2.remove_all(); m_kindex1 = 0.0; m_kindex1 = 0.0; m_status1 = initial; m_status2 = initial; } 

حدث خطأ في وظيفة إعادة الضبط : خطأ مطبعي في فهرس m_kindex2 لن يتم إعادة تعيين قيمة هذا المتغير ، مما قد يؤثر على تنفيذ أجزاء التعليمات البرمجية الأخرى.

V501 هناك تعبيرات فرعية متطابقة إلى اليسار وإلى يمين عامل التشغيل '>': fg [order_type :: R]> fg [order_type :: R] agg_span_image_filter_rgba.h 898

 typedef Source source_type; typedef typename source_type::color_type color_type; typedef typename source_type::order_type order_type; void generate(color_type* span, int x, int y, unsigned len) { .... if(fg[0] < 0) fg[0] = 0; if(fg[1] < 0) fg[1] = 0; if(fg[2] < 0) fg[2] = 0; if(fg[3] < 0) fg[3] = 0; if(fg[order_type::A] > base_mask) fg[order_type::A] = base_mask; if(fg[order_type::R] > fg[order_type::R])fg[order_type::R] = fg[order_type::R]; if(fg[order_type::G] > fg[order_type::G])fg[order_type::G] = fg[order_type::G]; if(fg[order_type::B] > fg[order_type::B])fg[order_type::B] = fg[order_type::B]; .... } 

في الأسطر الأخيرة ، هناك على الفور مقارنة بين نفس المتغيرات وتعيين نفس المتغيرات. لا أستطيع تحمل ما كان يعتقد هنا. فقط ضع علامة على الجزء المريب.

V570 يتم تعيين المتغير "wPipeIndex" لنفسه. CEchoGals_transport.cpp 244

 ECHOSTATUS CEchoGals::CloseAudio (....) { .... wPipeIndex = wPipeIndex; m_ProcessId[ wPipeIndex ] = NULL; m_Pipes[ wPipeIndex ].wInterleave = 0; .... } 

تتم تهيئة متغير wPipeIndex إلى قيمته الخاصة. على الأرجح ، تم إجراء خطأ مطبعي.

أخطاء مع المؤشرات


V522 قد يتم إلغاء تأشير المؤشر الخالي "currentInterface". Device.cpp 258

 Device::Device(....) : .... { .... usb_interface_info* currentInterface = NULL; // <= uint32 descriptorStart = sizeof(usb_configuration_descriptor); while (descriptorStart < actualLength) { switch (configData[descriptorStart + 1]) { .... case USB_DESCRIPTOR_ENDPOINT: { .... if (currentInterface == NULL) // <= break; currentInterface->endpoint_count++; .... } .... case USB_DESCRIPTOR_ENDPOINT_COMPANION: { usb_endpoint_descriptor* desc = currentInterface // <= ->endpoint[currentInterface->endpoint_count - 1].descr; .... } .... } 

يتم تهيئة مؤشر CurrentInterface في البداية إلى صفر ثم يتم التحقق منه عند إدخال فرع بيان التبديل ، ولكن ليس في جميع الحالات. يحذر المحلل من أنه عند الانتقال إلى تسمية USB_DESCRIPTOR_ENDPOINT_COMPANION ، من الممكن إلغاء تحديد مؤشر فارغ.

V522 قد يتم إلغاء تسجيل "دليل" المؤشر الخالي. PathMonitor.cpp 1465

 bool PathHandler::_EntryCreated(....) { .... Directory* directory = directoryNode->ToDirectory(); if (directory == NULL) { // We're out of sync with reality. if (!dryRun) { if (Entry* nodeEntry = directory->FirstNodeEntry()) { .... } } return false; } .... } 

أعتقد أن هناك خطأ في مقارنة مؤشر الدليل بقيمة فارغة ويجب أن يكون الشرط هو عكس ذلك. في التطبيق الحالي ، إذا كان المتغير dryRun خطأ ، فسيتم إلغاء تحديد مؤشر الدليل الخالي.

V522 قد يتم إلغاء إدخال مؤشر "الإدخال" الخالي من المؤشر. MediaRecorder.cpp 343

 void GetInput(media_input* input); const media_input& BMediaRecorder::MediaInput() const { CALLED(); media_input* input = NULL; fNode->GetInput(input); return *input; } 

تتم تهيئة مؤشر الإدخال إلى صفر ويبقى عند هذه القيمة ، لأن في دالة GetInput ، لا يتغير المؤشر. تكتب الطرق الأخرى لفئة BMediaRecorder بشكل مختلف ، على سبيل المثال:

 status_t BMediaRecorder::_Connect(....) { .... // Find our Node's free input media_input ourInput; fNode->GetInput(&ourInput); // <= .... } 

كل شيء صحيح هنا ، لكن من المستحيل أن تكتب هكذا في الجزء الأول من الكود ، وإلا فإن الوظيفة ستعيد رابطًا للكائن المحلي ، لكن بطريقة ما يجب أن تكون الشفرة ثابتة.

V522 قد يتم إلغاء تسجيل المؤشر الخالي "mustFree". RequestUnflattener.cpp 35

 status_t Reader::Read(int32 size, void** buffer, bool* mustFree) { if (size < 0 || !buffer || mustFree) // <= return B_BAD_VALUE; if (size == 0) { *buffer = NULL; *mustFree = false; // <= return B_OK; } .... } 

في التعبير الشرطي ، حيث يتم فحص جميع بيانات الإدخال غير الصالحة ، تم إجراء خطأ مطبعي عند التحقق من مؤشر mustFree . على الأرجح ، يجب أن يكون الخروج من الوظيفة بقيمة صفر لهذا المؤشر:

 if (size < 0 || !buffer || !mustFree) // <= return B_BAD_VALUE; 

V757 من الممكن مقارنة متغير غير صحيح مع nullptr بعد تحويل النوع باستخدام 'dynamic_cast'. خطوط التحقق: 474 ، 476. recovery.cpp 474

 void checkStructure(Disk &disk) { .... Inode* missing = gMissing.Get(run); dir = dynamic_cast<Directory *>(missing); if (missing == NULL) { .... } .... } 

بدلاً من المؤشر المفقود ، يجب عليك التحقق من مؤشر dir بعد تحويل النوع. بالمناسبة ، غالبًا ما يرتكب مبرمجو C # خطأ مشابهًا. هذا يثبت مرة أخرى أن بعض الأخطاء مستقلة عن اللغة المستخدمة.

زوجان أكثر أماكن مماثلة في الرمز:

  • V757 من الممكن مقارنة متغير غير صحيح مع nullptr بعد تحويل النوع باستخدام 'dynamic_cast'. خطوط التحقق: 355 ، 357. ExpandoMenuBar.cpp 355
  • V757 من الممكن مقارنة متغير غير صحيح مع nullptr بعد تحويل النوع باستخدام 'dynamic_cast'. خطوط التحقق: 600 ، 601. ValControl.cpp 600

أخطاء الفهرس


V557 تجاوز الصفيف هو ممكن. يشير مؤشر 'BT_SCO' إلى ما وراء مجموعة الصفيف. h2upper.cpp 75

 struct bt_usb_dev { .... struct list nbuffersTx[(1 + 1 + 0 + 0)]; // <= [0..1] .... } typedef enum { BT_COMMAND = 0, BT_EVENT, BT_ACL, BT_SCO, // <= 3 BT_ESCO, HCI_NUM_PACKET_TYPES } bt_packet_t; void sched_tx_processing(bt_usb_dev* bdev) { .... if (!list_is_empty(&bdev->nbuffersTx[BT_SCO])) { // <= fail // TODO to be implemented } .... } 

يتكون صفيف bdev-> nbuffersTx من عنصرين فقط ، بينما يتم الوصول إليه في الكود بواسطة BT_SCO الثابت ، والذي له قيمة 3. ويحدث خروج مضمون يتجاوز حدود المصفوفة.

V557 تجاوز الصفيف هو ممكن. تقوم دالة "ieee80211_send_setup" بمعالجة القيمة "16". تفقد الحجة الرابعة. خطوط التحقق: 842 ، 911. ieee80211_output.c 842

 struct ieee80211_node { .... struct ieee80211_tx_ampdu ni_tx_ampdu[16]; // <= [0..15] .... }; #define IEEE80211_NONQOS_TID 16 int ieee80211_mgmt_output(....) { .... ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | type, IEEE80211_NONQOS_TID, // <= 16 vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); .... } void ieee80211_send_setup( struct ieee80211_node *ni, struct mbuf *m, int type, int tid, // <= 16 ....) { .... tap = &ni->ni_tx_ampdu[tid]; // <= 16 .... } 

طريقة أخرى للخروج من مجموعة. في هذه الحالة ، عنصر واحد فقط. ساعد التحليل Interprocedural في تحديد موقف حيث تم الوصول إلى الصفيف ni-> ni_tx_ampdu ، الذي يتكون من 16 عنصرًا ، في الفهرس 16 . في C و C ++ ، تتم فهرسة الصفيف من البداية.

V781 يتم التحقق من قيمة المتغير 'vector' بعد استخدامه. ربما هناك خطأ في منطق البرنامج. خطوط التحقق: 802 ، 805. oce_if.c 802

 #define OCE_MAX_EQ 32 typedef struct oce_softc { .... OCE_INTR_INFO intrs[OCE_MAX_EQ]; .... } OCE_SOFTC, *POCE_SOFTC; static int oce_alloc_intr(POCE_SOFTC sc, int vector, void (*isr) (void *arg, int pending)) { POCE_INTR_INFO ii = &sc->intrs[vector]; int rc = 0, rr; if (vector >= OCE_MAX_EQ) return (EINVAL); .... } 

اكتشف المحلل مكالمة إلى مصفوفة sc-> intrs في فهرس غير صالح (يتجاوز حدود المصفوفة). السبب في ذلك هو ترتيب التعليمات البرمجية غير صحيح. هنا ، يتم الوصول إلى الصفيف لأول مرة ، ثم يتم إجراء التحقق لمعرفة ما إذا كانت قيمة الفهرس صالحة.

قد يقول شخص ما أنه لن تكون هناك مشكلة. هنا ، بعد كل شيء ، لا يتم استرداد قيمة عنصر الصفيف ، ولكن يتم ببساطة أخذ عنوان الخلية. لا ، لا يمكنك القيام بذلك على أي حال. اقرأ المزيد: "يؤدي إلغاء مؤشر مؤشر فارغ إلى سلوك غير محدد ."

V519 يتم تخصيص القيم مرتين على التوالي. ربما هذا خطأ. خطوط التحقق: 199 ، 200. nvme_ctrlr.c 200

 static void nvme_ctrlr_set_intel_supported_features(struct nvme_ctrlr *ctrlr) { bool *supported_feature = ctrlr->feature_supported; supported_feature[NVME_INTEL_FEAT_MAX_LBA] = true; supported_feature[NVME_INTEL_FEAT_MAX_LBA] = true; supported_feature[NVME_INTEL_FEAT_NATIVE_MAX_LBA] = true; supported_feature[NVME_INTEL_FEAT_POWER_GOVERNOR_SETTING] = true; supported_feature[NVME_INTEL_FEAT_SMBUS_ADDRESS] = true; supported_feature[NVME_INTEL_FEAT_LED_PATTERN] = true; supported_feature[NVME_INTEL_FEAT_RESET_TIMED_WORKLOAD_COUNTERS] = true; supported_feature[NVME_INTEL_FEAT_LATENCY_TRACKING] = true; } 

يتم تعيين عنصر الصفيف مع الفهرس NVME_INTEL_FEAT_MAX_LBA بنفس القيمة. لحسن الحظ ، يتم تمثيل جميع الثوابت الممكنة في هذه الوظيفة ، والكود هو ببساطة نتيجة لبرمجة Copy-Paste. لكن احتمال ارتكاب الأخطاء بهذه الطريقة مرتفع للغاية.

V519 يتم تعيين متغير "copiedPath [len]" قيم مرتين على التوالي. ربما هذا خطأ. خطوط التحقق: 92 ، 93. kernel_emu.cpp 93

 int UserlandFS::KernelEmu::new_path(const char *path, char **copy) { .... // append a dot, if desired if (appendDot) { copiedPath[len] = '.'; copiedPath[len] = '\0'; } .... } 

وهنا المطور لم يحالفه الحظ في النسخ. تتم إضافة الرمز "dot" إلى سطر معين ويتم استبداله على الفور بواسطة نقطة الصفر. على الأرجح ، قام مؤلف الكود بنسخ السلسلة ونسي زيادة الفهرس.

ظروف غريبة


V517 استخدام نمط "if (A) {...} آخر إذا تم اكتشاف (A) {...}". هناك احتمال لوجود خطأ منطقي. خطوط التحقق: 1407 ، 1410. FindPanel.cpp 1407

 void FindPanel::BuildAttrQuery(BQuery* query, bool &dynamicDate) const { .... case B_BOOL_TYPE: { uint32 value; if (strcasecmp(textControl->Text(), "true") == 0) { value = 1; } else if (strcasecmp(textControl->Text(), "true") == 0) { value = 1; } else value = (uint32)atoi(textControl->Text()); value %= 2; query->PushUInt32(value); break; } .... } 

نسخ الكود أدى على الفور إلى خطأين. التعبيرات الشرطية متطابقة. على الأرجح ، يجب أن يكون لدى أحدهم مقارنة بالسلسلة "false" ، بدلاً من "true". بعد ذلك ، في الفرع الذي يعالج القيمة "false" ، يجب عليك تغيير قيمة القيمة من 1 إلى 0 . تفترض الخوارزمية أنه سيتم تحويل أي قيم أخرى غير صحيحة أو خاطئة إلى رقم باستخدام وظيفة atoi ، ولكن بسبب الخطأ ، سيحصل النص أيضًا على النص "false".

تعبير V547 'error == ((int) 0)' صحيح دائمًا. Directory.cpp 688

 int32 BDirectory::CountEntries() { status_t error = Rewind(); if (error != B_OK) return error; int32 count = 0; BPrivate::Storage::LongDirEntry entry; while (error == B_OK) { if (GetNextDirents(&entry, sizeof(entry), 1) != 1) break; if (strcmp(entry.d_name, ".") != 0 && strcmp(entry.d_name, "..") != 0) count++; } Rewind(); return (error == B_OK ? count : error); } 

اكتشف المحلل أن قيمة متغير الخطأ ستكون دائمًا B_OK . على الأرجح ، تم تفويت تعديل لهذا المتغير في حلقة التكرار .

V564 يتم تطبيق عامل التشغيل "&" على قيمة نوع منطقي. ربما نسيت تضمين الأقواس أو المقصود استخدام عامل التشغيل "&&". strtod.c 545

 static int lo0bits(ULong *y) { int k; ULong x = *y; .... if (!(x & 1)) { k++; x >>= 1; if (!x & 1) // <= return 32; } *y = x; return k; } 

على الأرجح ، في التعبير الشرطي الأخير نسوا وضع الأقواس ، كما في الشروط أعلاه. ربما ، هناك ، أيضًا ، يجب أن يكون عامل النفي خارج الأقواس:

 if (!(x & 1)) // <= return 32; 

V590 النظر في فحص هذا التعبير. التعبير مفرط أو يحتوي على خطأ مطبعي. PoseView.cpp 5851

 bool BPoseView::AttributeChanged(const BMessage* message) { .... result = poseModel->OpenNode(); if (result == B_OK || result != B_BUSY) break; .... } 

هذا غير واضح ، لكن نتيجة الشرط لا تعتمد على قيمة B_OK. وبالتالي ، يمكن تبسيطها:

 If (result != B_BUSY) break; 

يمكن التحقق من ذلك بسهولة عن طريق إنشاء جدول الحقيقة لقيم متغير النتيجة . إذا كنت بحاجة إلى مراعاة القيم الأخرى بخلاف B_OK و B_BUSY ، فينبغي إعادة كتابة الرمز بطريقة مختلفة.

شرطين أكثر مماثلة:

  • V590 النظر في فحص هذا التعبير. التعبير مفرط أو يحتوي على خطأ مطبعي. Tracker.cpp 1714
  • V590 النظر في فحص هذا التعبير. التعبير مفرط أو يحتوي على خطأ مطبعي. if_ipw.c 1871

V590 خذ بعين الاعتبار فحص 'argc == 0 || argc! = 2 'تعبير. التعبير مفرط أو يحتوي على خطأ مطبعي. cmds.c 2667

 void unsetoption(int argc, char *argv[]) { .... if (argc == 0 || argc != 2) { fprintf(ttyout, "usage: %s option\n", argv[0]); return; } .... } 

ولعل هذا هو أبسط مثال يوضح عملية تشخيص V590 . من الضروري عرض وصف البرنامج في حالة عدم وجود وسيطات تم تمريرها أو عدم وجود اثنتين منها. من الواضح أن أي قيم بخلاف القيمتين ، بما في ذلك الصفر ، لن تفي بالشرط. لذلك ، يمكن تبسيط الحالة بسهولة لما يلي:

 if (argc != 2) { fprintf(ttyout, "usage: %s option\n", argv[0]); return; } 

V590 خذ بعين الاعتبار فحص '* ptr =='؛ ' && * ptr! = '\ 0' 'التعبير. التعبير مفرط أو يحتوي على خطأ مطبعي. pc.c 316

 ULONG parse_expression(char *str) { .... ptr = skipwhite(ptr); while (*ptr == SEMI_COLON && *ptr != '\0') { ptr++; if (*ptr == '\0') continue; val = assignment_expr(&ptr); } .... } 

في هذا المثال ، تم تغيير العامل المنطقي ، لكن المنطق بقي كما هو. هنا ، لا تعتمد حالة حلقة التكرار إلا على ما إذا كانت الشخصية تساوي SEMI_COLON أم لا.

V590 النظر في فحص هذا التعبير. التعبير مفرط أو يحتوي على خطأ مطبعي. writembr.cpp 99

 int main(int argc, char** argv) { .... string choice; getline(cin, choice, '\n'); if (choice == "no" || choice == "" || choice != "yes") { cerr << "MBR was NOT written" << endl; fs.close(); return B_ERROR; } .... } 

هناك بالفعل ثلاثة شروط في هذا المثال. يمكن تبسيطها أيضًا للتحقق مما إذا كان المستخدم قد اختار "نعم" أم لا:

 if (choice != "yes") { cerr << "MBR was NOT written" << endl; fs.close(); return B_ERROR; } 

أخطاء متنوعة


V530 يجب استخدام قيمة الإرجاع للدالة "start". IMAPFolder.cpp 414

 void IMAPFolder::RegisterPendingBodies(...., const BMessenger* replyTo) { .... IMAP::MessageUIDList::const_iterator iterator = uids.begin(); for (; iterator != uids.end(); iterator++) { if (replyTo != NULL) fPendingBodies[*iterator].push_back(*replyTo); else fPendingBodies[*iterator].begin(); // <= } } 

اكتشف المحلل مكالمة لا معنى لها لبدء التكرار (). لا يمكنني تخمين كيفية إصلاح الكود. يجب على المطور الانتباه إلى هذا المكان.

V609 قسّم على صفر. نطاق المقام [0..64]. UiUtils.cpp 544

 static int32 GetSIMDFormatByteSize(uint32 format) { switch (format) { case SIMD_RENDER_FORMAT_INT8: return sizeof(char); case SIMD_RENDER_FORMAT_INT16: return sizeof(int16); case SIMD_RENDER_FORMAT_INT32: return sizeof(int32); case SIMD_RENDER_FORMAT_INT64: return sizeof(int64); case SIMD_RENDER_FORMAT_FLOAT: return sizeof(float); case SIMD_RENDER_FORMAT_DOUBLE: return sizeof(double); } return 0; } const BString& UiUtils::FormatSIMDValue(const BVariant& value, uint32 bitSize, uint32 format, BString& _output) { _output.SetTo("{"); char* data = (char*)value.ToPointer(); uint32 count = bitSize / (GetSIMDFormatByteSize(format) * 8); // <= .... } 

ترجع الدالة GetSIMDFormatByteSize فعليًا 0 كقيمة افتراضية ، مما قد يؤدي إلى القسمة على صفر.

V654 الشرط 'specificSequence! = Sequence' of loop is false دائماً. pthread_key.cpp 55

 static void* get_key_value(pthread_thread* thread, uint32 key, int32 sequence) { pthread_key_data& keyData = thread->specific[key]; int32 specificSequence; void* value; do { specificSequence = keyData.sequence; if (specificSequence != sequence) return NULL; value = keyData.value; } while (specificSequence != sequence); keyData.value = NULL; return value; } 

المحلل هو الصحيح أن حالة من حين بيان دائما كاذبة. لهذا السبب ، لا يتم تنفيذ أكثر من تكرار واحد في حلقة. بمعنى آخر ، لن يتغير شيء إذا كتبت (0) . كل هذا غريب للغاية ويحتوي هذا الكود على نوع من الخطأ في منطق العمل. يجب على المطورين الانتباه إلى هذا المكان.

V672 ربما لا توجد حاجة لإنشاء متغير "المسار" الجديد هنا. إحدى وسيطات الوظيفة لها نفس الاسم وهذه الوسيطة هي مرجع. خطوط التحقق: 348 ، 429. translate.cpp 429

 status_t Translator::FindPath(...., TypeList &path, double &pathQuality) { .... TypeList path; double quality; if (FindPath(&formats[j], stream, typesSeen, path, quality) == B_OK) { if (bestQuality < quality * formatQuality) { bestQuality = quality * formatQuality; bestPath.SetTo(path); bestPath.Add(formats[j].type); status = B_OK; } } .... } 

يتم تمرير متغير المسار إلى وظيفة FindPath حسب المرجع. هذا يعني أنه من الممكن تعديل هذا المتغير في نص الدالة. ولكن هنا يوجد متغير محلي بنفس الاسم الذي يتم تعديله. في هذه الحالة ، ستبقى جميع التغييرات فقط في المتغير المحلي. ربما يجب عليك إعادة تسمية أو حذف المتغير المحلي.

V705 من الممكن أن تكون كتلة "آخر" قد تم نسيانها أو التعليق عليها ، مما أدى إلى تغيير منطق عمليات البرنامج. HostnameView.cpp 109

 status_t HostnameView::_LoadHostname() { BString fHostnameString; char hostname[MAXHOSTNAMELEN]; if (gethostname(hostname, MAXHOSTNAMELEN) == 0) { fHostnameString.SetTo(hostname, MAXHOSTNAMELEN); fHostname->SetText(fHostnameString); return B_OK; } else return B_ERROR; } 

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

تتم إعادة كتابة "قائمة" المعلمة V763 دائمًا في هيكل الوظيفة قبل استخدامها. video.cpp 648

 bool video_mode_hook(Menu *menu, MenuItem *item) { video_mode *mode = NULL; menu = item->Submenu(); item = menu->FindMarked(); .... } 

كانت هناك العديد من الحالات التي يتم فيها الكتابة على وسيطات الدالة فورًا عند مدخل الوظيفة. هذا السلوك مضلل للمطورين الآخرين الذين يقومون باستدعاء هذه الوظائف نفسها.

قائمة كاملة من الأماكن المشبوهة:

  • تتم إعادة كتابة المعلمة V763 'force_16bit' دائمًا في نصوص الوظيفة قبل استخدامها. ata_adapter.cpp 151
  • تتم إعادة كتابة المعلمة V763 'force_16bit' دائمًا في نصوص الوظيفة قبل استخدامها. ata_adapter.cpp 179
  • تتم إعادة كتابة "قائمة" المعلمة V763 دائمًا في هيكل الوظيفة قبل استخدامها. video.cpp 264
  • V763 يتم دائمًا إعادة كتابة المعلمة "الطول" في جسم الوظيفة قبل استخدامها. MailMessage.cpp 677
  • تتم إعادة كتابة "إدخال" المعلمة V763 دائمًا في نصوص الوظيفة قبل استخدامها. IconCache.cpp 773
  • تتم إعادة كتابة "إدخال" المعلمة V763 دائمًا في نصوص الوظيفة قبل استخدامها. IconCache.cpp 832
  • تتم إعادة كتابة "إدخال" المعلمة V763 دائمًا في نصوص الوظيفة قبل استخدامها. IconCache.cpp 864
  • تتم إعادة كتابة المعلمة V763 'rect' دائمًا في هيكل الوظيفة قبل استخدامها. ErrorLogWindow.cpp 56
  • تتم إعادة كتابة المعلمة V763 "updateRect" دائمًا في هيكل الوظيفة قبل استخدامها. CalendarMenuWindow.cpp 49
  • تتم إعادة كتابة المعلمة V763 'rect' دائمًا في هيكل الوظيفة قبل استخدامها. MemoryView.cpp 165
  • تتم إعادة كتابة المعلمة V763 'rect' دائمًا في هيكل الوظيفة قبل استخدامها. TypeEditors.cpp 1124
  • V763 يتم دائمًا إعادة كتابة المعلمة "الارتفاع" في جسم الوظيفة قبل استخدامها. Workspaces.cpp 857
  • V763 يتم دائمًا إعادة كتابة المعلمة "عرض" في نصوص الوظيفة قبل استخدامها. Workspaces.cpp 856
  • تتم إعادة كتابة "الإطار" للمعلمة V763 دائمًا في هيكل الوظيفة قبل استخدامه. SwatchGroup.cpp 48
  • تتم إعادة كتابة "الإطار" للمعلمة V763 دائمًا في هيكل الوظيفة قبل استخدامه. PlaylistWindow.cpp 89
  • تتم إعادة كتابة المعلمة V763 'rect' دائمًا في هيكل الوظيفة قبل استخدامها. ConfigView.cpp 78
  • تتم إعادة كتابة المعلمة V763 دائمًا في جسم الوظيفة قبل استخدامها. mkntfs.c 3917
  • تتم إعادة كتابة المعلمة V763 "rxchainmask" دائمًا في هيكل الوظيفة قبل استخدامها. ar5416_cal.c 463
  • تتم إعادة كتابة المعلمة V763 دائمًا في جسم الوظيفة قبل استخدامها. if_iwn.c 6854

استنتاج


مشروع هايكو هو مصدر البق مثيرة للاهتمام ونادرة. لقد قمنا بتجديد قاعدة بياناتنا لأمثلة عن الأخطاء وحلنا العديد من المشكلات في المحلل المحدد خلال تحليل الكود.

إذا لم تقم بفحص الكود الخاص بك باستخدام أي من أدوات تحليل الشفرة لفترة طويلة ، فمن المحتمل أن يكون هناك شيء ما هو موضح في الكود أيضًا. استخدم PVS-Studio في مشروعك للتحكم في جودة الشفرة إذا كانت مكتوبة بلغات C أو C ++ أو C # أو Java. يمكنك تنزيل المحلل دون تسجيل ورسالة نصية قصيرة هنا .

تريد أن تجرب هايكو ولديك أي أسئلة؟ يدعوك مطورو Haiku إلى قناة التلغراف .



إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فالرجاء استخدام الرابط الخاص بالترجمة: Svyatoslav Razmyslov. أفضل خوارزميات نسخ ولصق لـ C و C ++. هايكو OS كتاب الطبخ

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


All Articles