سؤال

لذا، أنا بحاجة إلى بعض المساعدة.أنا أعمل على مشروع بلغة C++.ومع ذلك، أعتقد أنني تمكنت بطريقة أو بأخرى من إفساد الكومة الخاصة بي.هذا يعتمد على حقيقة أنني أضفت std::string إلى فئة وتعيين قيمة لها من أخرى std::string:

std::string hello = "Hello, world.\n";
/* exampleString = "Hello, world.\n" would work fine. */
exampleString = hello;

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

ومع ذلك، فأنا في حيرة من أمري مع هذا النوع من الأشياء، لذا فكرت في التخلص منها هناك.أنا أستخدم نظام Linux وقد قمت بالتجول معه valgrind, ، وعلى الرغم من أنني لا أعرف تمامًا ما أفعله، فقد أبلغت عن ذلك std::stringكان المدمر الخاص بـ مجانيًا غير صالح.يجب أن أعترف أنني حصلت على مصطلح "فساد الكومة" من بحث Google؛سيكون موضع تقدير أيضًا أي مقالات للأغراض العامة حول هذا النوع من الأشياء.

(في قبل rm -rf ProjectDir, ، قم بذلك مرة أخرى في C# :D)

يحرر:لم أوضح الأمر، ولكن ما أطلبه هو طرق ونصائح لتشخيص هذا النوع من مشاكل الذاكرة.أعلم أن الأشياء std::string صحيحة، لذا فهذا شيء قمت به (أو خطأ، ولكن لا توجد مشكلة في التحديد).أنا متأكد من أنني أستطيع التحقق من الكود الذي كتبته وسوف ترى أيها الأشخاص الأذكياء المشكلة في وقت قصير، ولكنني أريد إضافة هذا النوع من تحليل الكود إلى "صندوق الأدوات" الخاص بي، كما كان.

هل كانت مفيدة؟

المحلول

هذه آليات رخيصة نسبيًا لحل المشكلة:

  1. إبقاء العين على بلدي سؤال الفساد الكومة - أقوم بتحديث الإجابات أثناء التخلص منها.الأول كان التوازن new[] و delete[], ، لكنك تفعل ذلك بالفعل.
  2. يعطي valgrind أكثر من الذهاب.إنها أداة ممتازة، وأتمنى فقط أن تكون متاحة ضمن نظام التشغيل Windows.أنا فقط أبطئ برنامجك بمقدار النصف تقريبًا، وهو أمر جيد جدًا مقارنة بمثيلاته في Windows.
  3. فكر في استخدام أدوات أداء جوجل كبديل malloc/جديد.
  4. هل قمت بتنظيف جميع ملفات الكائنات الخاصة بك وبدأت من جديد؟ربما يكون ملف الصنع الخاص بك هو ..."دون المستوى الأمثل"
  5. أنت لست assert()ما يكفي في التعليمات البرمجية الخاصة بك.وكيف أعرف ذلك دون أن أراه؟مثل الخيط، لا أحد assert()يكفي في التعليمات البرمجية الخاصة بهم.أضف وظيفة التحقق من صحة الكائنات الخاصة بك واستدعها عند بداية الطريقة ونهاية الطريقة.
  6. أنت تجميع -wall؟إذا لم يكن الأمر كذلك، افعل ذلك.
  7. ابحث عن أداة الوبر مثل بي سي لينت.قد يتناسب تطبيق صغير مثل تطبيقك مع عرض PC-lint الصفحة، مما يعني عدم الشراء لك!
  8. تأكد من إلغاء المؤشرات بعد حذفها.لا أحد يحب المؤشر المتدلي.نفس الحفلة مع المؤشرات المعلنة ولكن غير المخصصة.
  9. التوقف عن استخدام المصفوفات.إستخدم المتجه بدلاً من.
  10. لا تستخدم المؤشرات الخام.إستخدم مؤشر ذكي.لا تستخدم auto_ptr!هذا الشيء هو...مفاجئ؛دلالاتها غريبة جدا.بدلاً من ذلك، اختر واحدة من تعزيز المؤشرات الذكية, ، أو شيء من مكتبة لوكي.

نصائح أخرى

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

في النهاية قمنا بتتبعه باستخدام نقاط مراقبة مصحح الأخطاء.سأحاول وصف الإجراء هنا:

1) ابحث عن سبب الفشل.يبدو من رمز المثال الخاص بك أن ذاكرة "exampleString" تالفة، وبالتالي لا يمكن الكتابة إليها.دعونا نواصل هذا الافتراض.

2) قم بتعيين نقطة توقف عند آخر موقع معروف يتم فيه استخدام "exampleString" أو تعديله دون أي مشكلة.

3) أضف نقطة مراقبة إلى عضو البيانات في "exampleString".مع الإصدار الخاص بي من g++، يتم تخزين السلسلة في _M_dataplus._M_p.نريد أن نعرف متى يتغير عضو البيانات هذا.تقنية GDB لهذا هي:

(gdb) p &exampleString._M_dataplus._M_p
$3 = (char **) 0xbfccc2d8
(gdb)  watch *$3
Hardware watchpoint 1: *$3

من الواضح أنني أستخدم Linux مع g++ وgdb هنا، لكنني أعتقد أن نقاط مراقبة الذاكرة متوفرة مع معظم مصححات الأخطاء.

4) استمر حتى يتم تشغيل نقطة المراقبة:

Continuing.
Hardware watchpoint 2: *$3

Old value = 0xb7ec2604 ""
New value = 0x804a014 ""
0xb7e70a1c in std::string::_M_mutate () from /usr/lib/libstdc++.so.6
(gdb) where

جي دي بي where سيعطي الأمر تتبعًا خلفيًا يوضح ما أدى إلى التعديل.يعد هذا تعديلًا قانونيًا تمامًا، وفي هذه الحالة استمر فقط - أو إذا كنت محظوظًا، فسيكون التعديل بسبب تلف الذاكرة.في الحالة الأخيرة، يجب أن تكون الآن قادرًا على مراجعة الكود حقًا تسبب المشكلة ونأمل في حلها.

كان سبب الخطأ لدينا هو الوصول إلى مصفوفة ذات فهرس سلبي.كان الفهرس نتيجة تحويل المؤشر إلى وحدات "int" بحجم المصفوفة.تم تفويت الخطأ بواسطة valgrind et al.نظرًا لأن عناوين الذاكرة المخصصة عند التشغيل ضمن تلك الأدوات لم تكن أبدًا "> MAX_INT"ولذا لم ينتج عنه مؤشر سلبي أبدًا.

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

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

  1. احصل على الراحة في مصحح الأخطاء.
  2. ابدأ بالتجول في مصحح الأخطاء لمعرفة ما إذا كان يمكنك العثور على أي شيء يبدو مريبًا.تحقق بشكل خاص لمعرفة ما يحدث أثناء exampleString = hello; خط.
  3. تحقق للتأكد من تعطله فعليًا exampleString = hello; الخط، وليس عند الخروج من بعض الكتل المحيطة (مما قد يتسبب في إطلاق المدمرات).
  4. تحقق من أي مؤشر سحري قد تقوم به.المؤشر الحسابي، والصب، وما إلى ذلك.
  5. تحقق من جميع التخصيصات والتخصيصات الخاصة بك للتأكد من مطابقتها (لا يوجد إلغاء التخصيص المزدوج).
  6. تأكد من عدم إرجاع أي مراجع أو مؤشرات إلى الكائنات الموجودة في المكدس.

هناك الكثير من الأشياء الأخرى التي يمكنك تجربتها أيضًا.أنا متأكد من أن بعض الأشخاص الآخرين سوف ينسجمون مع الأفكار أيضًا.

بعض الأماكن للبدء:

إذا كنت تستخدم نظام التشغيل windows وتستخدم Visual C++ 6 (آمل من الله أن لا أحد يستخدمه هذه الأيام) فإن تطبيق std::string ليس آمنًا على الخيط، ويمكن أن يؤدي إلى هذا النوع من الأشياء.

إليك مقالة وجدتها تشرح الكثير من الأسباب الشائعة لتسرب الذاكرة وفسادها.

في مكان عملي السابق استخدمنا Compuware Boundschecker للمساعدة في هذا الأمر.إنه تجاري ومكلف للغاية، لذا قد لا يكون خيارًا.

إليك بعض المكتبات المجانية التي قد تكون ذات فائدة ما

http://www.codeguru.com/cpp/misc/misc/memory/article.php/c3745/

http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx

امل ان يساعد.إن فساد الذاكرة هو مكان مقرف للتواجد فيه!

يمكن أن يكون فسادًا مكدسًا، ولكن من المحتمل أيضًا أن يكون فسادًا مكدسًا.جيم على حق.نحن حقا بحاجة إلى مزيد من السياق قليلا.لا يخبرنا هذان الخطان من المصدر كثيرًا بمعزل عنهما.يمكن أن يكون هناك أي عدد من الأشياء التي تسبب هذا (وهذا هو الفرح الحقيقي لـ C/C++).

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

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

لقد قمت أيضًا بإصلاح مشكلة std::string.كيف؟عن طريق استبدالها بمتجه وتجميعها ثم استبدال السلسلة مرة أخرى.هو - هي كان يتعطل باستمرار هناك، وقد تم إصلاح ذلك على الرغم من أنه... لم يستطع.هناك شيء سيئ هناك، ولست متأكدا ما هو.كنت أرغب في التحقق من المرة الوحيدة التي قمت فيها بتخصيص الذاكرة يدويًا على الكومة، على الرغم من ذلك:

 this->map = new Area*[largestY + 1];
 for (int i = 0; i < largestY + 1; i++) {
     this->map[i] = new Area[largestX + 1];
 }

وحذفه:

for (int i = 0; i < largestY + 1; i++) {
    delete [] this->map[i];
}
delete [] this->map;

لم أقم بتخصيص مصفوفة ثنائية الأبعاد باستخدام C++ من قبل.يبدو أن العمل.

لقد قمت أيضًا بإصلاح مشكلة std::string.كيف؟عن طريق استبدالها بمتجه وتجميعها ثم استبدال السلسلة مرة أخرى.لقد كان يتعطل بشكل مستمر هناك، وتم إصلاح ذلك على الرغم من أنه... لم يستطع.هناك شيء سيئ هناك، ولست متأكدا ما هو.

يبدو أنك قد هزت دجاجة بالفعل.إذا كنت لا تعرف لماذا إنه يعمل الآن، ثم لا يزال مكسورًا، ومن المؤكد أنه سيعضك مرة أخرى لاحقًا (بعد إضافة المزيد من التعقيد).

تشغيل التطهير.

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

إنه يعمل على مستوى كود الجهاز، لذلك لا تحتاج حتى إلى الحصول على كود المصدر.

إحدى أكثر المكالمات الجماعية الممتعة التي أجريتها على الإطلاق كانت عندما وجدت شركة Purify تسربًا للذاكرة في الكود الخاص بها، وتمكنا من طرح السؤال، "هل من الممكن أنك لا تقوم بتحرير الذاكرة في وظيفتك foo()" وسماع الدهشة في أصواتهم.

لقد ظنوا أننا نقوم بتصحيح أخطاء الآلهة ولكن بعد ذلك سمحنا لهم بالتعرف على السر حتى يتمكنوا من تشغيل Purify قبل أن نضطر إلى استخدام الكود الخاص بهم.:-)

http://www-306.ibm.com/software/awdtools/purify/unix/

(إنها باهظة الثمن ولكن لديهم تنزيل تقييم مجاني)

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

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

لاحظ أنه إذا كان برنامجك متعدد مؤشرات الترابط، فمن المحتمل أن يكون لديك مشكلات أكبر.إذا لم يكن الأمر كذلك، فيجب أن تكون قادرًا على تضييق نطاق الأمر بهذه الطريقة.حظ سعيد!

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

يعد تلف الذاكرة أحد أصعب الأشياء التي يجب استكشافها وإصلاحها وعادةً ما يتم حل هذه الأنواع من المشكلات عن طريق قضاء ساعات/أيام في مصحح الأخطاء وملاحظة شيء مثل "مرحبًا، يتم استخدام المؤشر X بعد حذفه!".

إذا كان ذلك مفيدًا، فهو شيء تتحسن فيه مع اكتسابك الخبرة.

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

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

إذا لم تكن قد حاولت بالفعل، فقم بتثبيت gdb (مصحح أخطاء gcc) وقم بتجميع البرنامج باستخدام -g.سيؤدي هذا إلى تجميع رموز التصحيح التي يمكن أن يستخدمها gdb.بمجرد تثبيت gdb، قم بتشغيله باستخدام البرنامج (gdb ). هذا يعد برنامج غش مفيدًا لاستخدام gdb.

قم بتعيين نقطة توقف للوظيفة التي تنتج الخطأ، وانظر ما هي قيمة exampleString.افعل نفس الشيء أيضًا مع أي معلمة تقوم بتمريرها إلى exampleString.يجب أن يخبرك هذا على الأقل ما إذا كانت السلاسل std::strings صالحة أم لا.

لقد وجدت الجواب من هذا المقال ليكون دليلا جيدا حول المؤشرات.

بقدر ما أستطيع أن أقول أن الكود الخاص بك صحيح.بافتراض أن exampleString عبارة عن سلسلة std::string لها نطاق فئة كما تصفها، يجب أن تكون قادرًا على تهيئتها/تخصيصها بهذه الطريقة.ربما هناك مشكلة أخرى؟ربما يساعد مقتطف من الكود الفعلي في وضعه في السياق.

سؤال:هل exampleString هو مؤشر لكائن سلسلة تم إنشاؤه باستخدام جديد؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top